Java tutorial
/******************************************************************************* * Copyright (c) 2011 Google, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.wb.internal.swing.model.layout.spring; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.eclipse.wb.core.controls.CCombo3; import org.eclipse.wb.draw2d.IPositionConstants; import org.eclipse.wb.internal.core.gef.policy.snapping.PlacementUtils; import org.eclipse.wb.internal.core.model.JavaInfoEvaluationHelper; import org.eclipse.wb.internal.core.model.JavaInfoUtils; import org.eclipse.wb.internal.core.model.property.Property; import org.eclipse.wb.internal.core.model.property.converter.IntegerConverter; import org.eclipse.wb.internal.core.model.property.editor.AbstractComboPropertyEditor; import org.eclipse.wb.internal.core.model.property.editor.IntegerPropertyEditor; import org.eclipse.wb.internal.core.model.property.editor.StaticFieldPropertyEditor; import org.eclipse.wb.internal.core.utils.ast.AstEditor; import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils; import org.eclipse.wb.internal.core.utils.ast.DomGenerics; import org.eclipse.wb.internal.core.utils.ast.NodeTarget; import org.eclipse.wb.internal.core.utils.ast.StatementTarget; import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx; import org.eclipse.wb.internal.core.utils.jdt.core.CodeUtils; import org.eclipse.wb.internal.swing.model.component.ComponentInfo; import org.eclipse.wb.internal.swing.model.component.ContainerInfo; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Statement; import org.apache.commons.lang.StringUtils; import java.text.MessageFormat; import java.util.List; import javax.swing.SpringLayout; /** * Model for attachment in {@link SpringLayout}. * * @author scheglov_ke * @coverage swing.model.layout */ public final class SpringAttachmentInfo { private static final String PUT_CONSTRAINT = "putConstraint(java.lang.String,java.awt.Component,int,java.lang.String,java.awt.Component)"; //////////////////////////////////////////////////////////////////////////// // // Instance // //////////////////////////////////////////////////////////////////////////// /** * @return the {@link SpringAttachmentInfo} for side of component. * * @param side * one of the {@link IPositionConstants#LEFT}, {@link IPositionConstants#TOP}, * {@link IPositionConstants#RIGHT}, {@link IPositionConstants#BOTTOM}. */ public static SpringAttachmentInfo get(SpringLayoutInfo layout, ComponentInfo component, int side) { String key = getAttachmentKey(side); SpringAttachmentInfo attachment = (SpringAttachmentInfo) component.getArbitraryValue(key); if (attachment == null) { attachment = new SpringAttachmentInfo(layout, component, side); component.putArbitraryValue(key, attachment); } return attachment; } private static String getAttachmentKey(int side) { return "SpringLayout_" + side; } //////////////////////////////////////////////////////////////////////////// // // Instance fields // //////////////////////////////////////////////////////////////////////////// private final SpringLayoutInfo m_layout; private final AstEditor m_editor; private final ComponentInfo m_component; private final int m_side; private final String m_springSide; private MethodInvocation m_invocation; private int m_offset; private ComponentInfo m_anchorComponent; private int m_anchorSide; //////////////////////////////////////////////////////////////////////////// // // Constructor // //////////////////////////////////////////////////////////////////////////// private SpringAttachmentInfo(SpringLayoutInfo layout, ComponentInfo component, int side) { m_layout = layout; m_editor = layout.getEditor(); m_component = component; m_side = side; m_springSide = getSpringSide(side); setInitialState(); } //////////////////////////////////////////////////////////////////////////// // // Initialization // //////////////////////////////////////////////////////////////////////////// private void setInitialState() { m_invocation = getInvocation(); if (m_invocation != null) { List<Expression> arguments = DomGenerics.arguments(m_invocation); Expression anchorExpression = arguments.get(4); Expression anchorSideExpression = arguments.get(3); Expression offsetExpression = arguments.get(2); m_anchorComponent = (ComponentInfo) m_layout.getContainer().getChildRepresentedBy(anchorExpression); { String springSide = (String) JavaInfoEvaluationHelper.getValue(anchorSideExpression); m_anchorSide = getFrameworkSide(springSide); } m_offset = (Integer) JavaInfoEvaluationHelper.getValue(offsetExpression); } } private void materialize() { if (m_invocation == null) { m_offset = 0; m_anchorComponent = m_layout.getContainer(); m_anchorSide = m_side; } } private MethodInvocation getInvocation() { List<MethodInvocation> invocations = m_layout.getMethodInvocations(PUT_CONSTRAINT); for (MethodInvocation invocation : invocations) { List<Expression> arguments = DomGenerics.arguments(invocation); Expression componentArgument = arguments.get(1); Expression sideArgument = arguments.get(0); if (m_component.isRepresentedBy(componentArgument) && m_springSide.equals(JavaInfoEvaluationHelper.getValue(sideArgument))) { return invocation; } } return null; } //////////////////////////////////////////////////////////////////////////// // // Utils // //////////////////////////////////////////////////////////////////////////// /** * Converts {@link IPositionConstants} side into {@link SpringLayout} side. */ public static String getSpringSide(int side) { switch (side) { case IPositionConstants.LEFT: return SpringLayout.WEST; case IPositionConstants.TOP: return SpringLayout.NORTH; case IPositionConstants.RIGHT: return SpringLayout.EAST; case IPositionConstants.BOTTOM: return SpringLayout.SOUTH; } throw new IllegalArgumentException(MessageFormat.format("Invalid side: {0}", side)); } /** * Converts {@link IPositionConstants} side into source for {@link SpringLayout} side. */ public static String getSpringSideSource(int side) { String springSideName; switch (side) { case IPositionConstants.LEFT: springSideName = "WEST"; break; case IPositionConstants.TOP: springSideName = "NORTH"; break; case IPositionConstants.RIGHT: springSideName = "EAST"; break; case IPositionConstants.BOTTOM: springSideName = "SOUTH"; break; default: throw new IllegalArgumentException(MessageFormat.format("Invalid side: {0}", side)); } return "javax.swing.SpringLayout." + springSideName; } /** * @param side * the side from {@link SpringLayout}. * * @return the absolute framework side one of the {@link IPositionConstants#LEFT}, * {@link IPositionConstants#TOP}, {@link IPositionConstants#RIGHT}, * {@link IPositionConstants#BOTTOM}. */ public static int getFrameworkSide(String side) { if (SpringLayout.WEST.equals(side)) { return IPositionConstants.LEFT; } if (SpringLayout.NORTH.equals(side)) { return IPositionConstants.TOP; } if (SpringLayout.EAST.equals(side)) { return IPositionConstants.RIGHT; } if (SpringLayout.SOUTH.equals(side)) { return IPositionConstants.BOTTOM; } throw new IllegalArgumentException(MessageFormat.format("Invalid side: {0}", side)); } //////////////////////////////////////////////////////////////////////////// // // Access // //////////////////////////////////////////////////////////////////////////// /** * @return the side of this attachment. */ public int getSide() { return m_side; } /** * @return <code>true</code> if this attachment is not represented in source. */ public boolean isVirtual() { return m_invocation == null; } public ComponentInfo getAnchorComponent() { return m_anchorComponent; } public void setAnchorComponent(ComponentInfo anchorComponent) { m_anchorComponent = anchorComponent; } public int getAnchorSide() { return m_anchorSide; } public void setAnchorSide(int anchorSide) { m_anchorSide = anchorSide; } /** * @return the offset from anchor. */ public int getOffset() { return m_offset; } public void setOffset(int offset) { m_offset = offset; } //////////////////////////////////////////////////////////////////////////// // // Write // //////////////////////////////////////////////////////////////////////////// /** * Deletes this attachment, so makes it virtual. */ public void delete() throws Exception { if (m_invocation != null) { m_editor.removeEnclosingStatement(m_invocation); m_invocation = null; m_anchorComponent = null; } } /** * Writes changes from fields to AST/source. */ public void write() throws Exception { NodeTarget target = getRequiredNodeTarget(); String componentSource = m_component.getVariableSupport().getReferenceExpression(target); // ensure putConstraint() if (m_invocation == null) { String arguments = StringUtils.join(new String[] { getSpringSideSource(m_side), componentSource, "0", "(java.lang.String) null", "null" }, ", "); StatementTarget statementTarget = target.getStatementTarget(); m_invocation = m_layout.addMethodInvocation(statementTarget, PUT_CONSTRAINT, arguments); m_component.addRelatedNodes(m_invocation); } // move putConstraint() to target { Statement invocationStatement = AstNodeUtils.getEnclosingStatement(m_invocation); StatementTarget statementTarget = target.getStatementTarget(); m_editor.moveStatement(invocationStatement, statementTarget); } // update putConstraint() { String offsetSource = IntegerConverter.INSTANCE.toJavaSource(m_layout, m_offset); String anchorSideSource = getSpringSideSource(m_anchorSide); String anchorComponentSource = m_anchorComponent.getVariableSupport().getReferenceExpression(target); // replace arguments List<Expression> arguments = DomGenerics.arguments(m_invocation); m_editor.replaceExpression(arguments.get(1), componentSource); m_editor.replaceExpression(arguments.get(2), offsetSource); m_editor.replaceExpression(arguments.get(3), anchorSideSource); m_editor.replaceExpression(arguments.get(4), anchorComponentSource); // add related node m_component.addRelatedNodes(m_invocation); m_anchorComponent.addRelatedNodes(m_invocation); } } /** * Does refreshing after attachment's write(). */ private void writeAndRefresh() throws Exception { write(); ExecutionUtils.refresh(m_component); } /** * Component or its anchor was moved (in same parent), so this attachment should be updated. */ public void adjustAfterComponentMove() throws Exception { write(); } /** * @return the {@link StatementTarget} based {@link NodeTarget} such that layout, component and * anchor can be referenced at it. */ private NodeTarget getRequiredNodeTarget() throws Exception { StatementTarget statementTarget = JavaInfoUtils .getStatementTarget_whenAllCreated(ImmutableList.of(m_layout, m_component, m_anchorComponent)); statementTarget = updateTargetToSortAttachments(statementTarget); return new NodeTarget(statementTarget); } //////////////////////////////////////////////////////////////////////////// // // Side ordering utils // //////////////////////////////////////////////////////////////////////////// private static final int[] sides = { IPositionConstants.TOP, IPositionConstants.LEFT, IPositionConstants.BOTTOM, IPositionConstants.RIGHT }; private StatementTarget updateTargetToSortAttachments(StatementTarget target) { Statement statement = target.getStatement(); if (target.isAfter() && statement != null) { while (true) { Statement nextStatement = getNextStatement_ifBeforeSide(statement); if (nextStatement != statement) { statement = nextStatement; } else { break; } } } target = new StatementTarget(statement, false); return target; } private Statement getNextStatement_ifBeforeSide(Statement statement) { Statement nextStatement = AstNodeUtils.getNextStatement(statement); SpringAttachmentInfo attachment = getCorrespondingSide(nextStatement); if (attachment != null) { if (isBeforeSide(attachment.m_side, m_side)) { statement = nextStatement; } } return statement; } private SpringAttachmentInfo getCorrespondingSide(Statement statement) { for (int i = 0; i < sides.length; i++) { int side = sides[i]; SpringAttachmentInfo attachment = m_layout.getAttachment(m_component, side); MethodInvocation invocation = attachment.m_invocation; if (invocation != null) { if (AstNodeUtils.getEnclosingStatement(invocation) == statement) { return attachment; } } } return null; } private static boolean isBeforeSide(int side, int nextSide) { return getSideOrder(side) - getSideOrder(nextSide) < 0; } private static int getSideOrder(int side) { for (int i = 0; i < sides.length; i++) { if (sides[i] == side) { return 1 + i; } } return 0; } //////////////////////////////////////////////////////////////////////////// // // Properties // //////////////////////////////////////////////////////////////////////////// protected Property[] getProperties() throws Exception { // offset Property offsetProperty = new Property(IntegerPropertyEditor.INSTANCE) { @Override public String getTitle() { return "offset"; } @Override public boolean isModified() throws Exception { return !isVirtual(); } @Override public Object getValue() throws Exception { if (isVirtual()) { return null; } return getOffset(); } @Override public void setValue(Object value) throws Exception { if (value instanceof Integer) { materialize(); setOffset((Integer) value); writeAndRefresh(); } } }; // anchor component Property anchorProperty = new Property(new ComponentEditor()) { @Override public String getTitle() { return "anchor"; } @Override public boolean isModified() throws Exception { return !isVirtual(); } @Override public Object getValue() throws Exception { if (isVirtual()) { return null; } return getAnchorComponent(); } @Override public void setValue(Object value) throws Exception { if (value instanceof ComponentInfo) { materialize(); setAnchorComponent((ComponentInfo) value); writeAndRefresh(); } } }; // anchor side Property sideProperty; { StaticFieldPropertyEditor editor = new StaticFieldPropertyEditor(); String[] fieldDescriptions = PlacementUtils.isHorizontalSide(getSide()) ? new String[] { "EAST", "WEST", } : new String[] { "NORTH", "SOUTH" }; editor.configure(SpringLayout.class, fieldDescriptions); sideProperty = new Property(editor) { @Override public String getTitle() { return "side"; } @Override public boolean isModified() throws Exception { return !isVirtual(); } @Override public Object getValue() throws Exception { if (isVirtual()) { return null; } return getSpringSide(getAnchorSide()); } @Override public void setValue(Object value) throws Exception { if (value instanceof String) { materialize(); setAnchorSide(getFrameworkSide((String) value)); writeAndRefresh(); } } }; } return new Property[] { offsetProperty, anchorProperty, sideProperty }; } //////////////////////////////////////////////////////////////////////////// // // Presentation // //////////////////////////////////////////////////////////////////////////// @Override public String toString() { if (isVirtual()) { return "<none>"; } else { return ExecutionUtils.runObjectIgnore(new RunnableObjectEx<String>() { public String runObject() throws Exception { return "(" + getOffset() + ", " + CodeUtils.getShortClass(getSpringSideSource(getAnchorSide())) + ", " + getAnchorComponent().getPresentation().getText() + ")"; } }, "<exception>"); } } //////////////////////////////////////////////////////////////////////////// // // Inner classes // //////////////////////////////////////////////////////////////////////////// /** * An editor for choosing components in properties. */ private final class ComponentEditor extends AbstractComboPropertyEditor { private final List<ComponentInfo> m_components = Lists.newArrayList(); //////////////////////////////////////////////////////////////////////////// // // Presentation // //////////////////////////////////////////////////////////////////////////// @Override protected String getText(Property property) throws Exception { ComponentInfo component = (ComponentInfo) property.getValue(); if (component != null) { return getText(component); } return null; } //////////////////////////////////////////////////////////////////////////// // // Combo // //////////////////////////////////////////////////////////////////////////// @Override protected void addItems(Property property, CCombo3 combo) throws Exception { m_components.clear(); // parent { ContainerInfo container = m_layout.getContainer(); m_components.add(container); combo.add(getText(container)); } // siblings List<ComponentInfo> components = m_layout.getComponents(); for (ComponentInfo component : components) { if (component != m_component) { m_components.add(component); combo.add(getText(component)); } } } @Override protected void selectItem(Property property, CCombo3 combo) throws Exception { combo.setText(getText(property)); } @Override protected void toPropertyEx(Property property, CCombo3 combo, int index) throws Exception { ComponentInfo component = m_components.get(index); property.setValue(component); } //////////////////////////////////////////////////////////////////////////// // // Utils // //////////////////////////////////////////////////////////////////////////// private String getText(ComponentInfo component) throws Exception { return component.getPresentation().getText(); } } }