| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * 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 com.intellij.uiDesigner.propertyInspector; |
| |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.uiDesigner.SwingProperties; |
| import com.intellij.uiDesigner.UIFormXmlConstants; |
| import com.intellij.uiDesigner.XmlWriter; |
| import com.intellij.uiDesigner.radComponents.RadComponent; |
| import com.intellij.uiDesigner.radComponents.RadContainer; |
| import com.intellij.uiDesigner.radComponents.RadGridLayoutManager; |
| import com.intellij.uiDesigner.snapShooter.SnapshotContext; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public abstract class IntrospectedProperty<V> extends Property<RadComponent, V> { |
| protected final static Object[] EMPTY_OBJECT_ARRAY=new Object[]{}; |
| |
| /** |
| * This method is used to set property value to "delegee" JComponent |
| */ |
| @NotNull protected final Method myReadMethod; |
| /** |
| * This method is used to get property value from "delegee" JComponent |
| */ |
| @NotNull private final Method myWriteMethod; |
| |
| private final boolean myStoreAsClient; |
| |
| @NonNls private static final String INTRO_PREFIX = "Intro:"; |
| |
| public IntrospectedProperty(final String name, |
| @NotNull final Method readMethod, |
| @NotNull final Method writeMethod, |
| final boolean storeAsClient) { |
| super(null, name); |
| myReadMethod = readMethod; |
| myWriteMethod = writeMethod; |
| myStoreAsClient = storeAsClient; |
| } |
| |
| /** |
| * <b>Do not overide this method without serious reason!</b> |
| */ |
| public V getValue(final RadComponent component){ |
| //noinspection unchecked |
| return (V)invokeGetter(component); |
| } |
| |
| protected Object invokeGetter(final RadComponent component) { |
| if (myStoreAsClient) { |
| return component.getClientProperty(INTRO_PREFIX + getName()); |
| } |
| try { |
| myReadMethod.setAccessible(true); |
| return myReadMethod.invoke(component.getDelegee(), EMPTY_OBJECT_ARRAY); |
| } |
| catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * <b>Do not overide this method without serious reason!</b> |
| */ |
| protected void setValueImpl(final RadComponent component,final V value) throws Exception{ |
| invokeSetter(component, value); |
| } |
| |
| protected void invokeSetter(final RadComponent component, final Object value) throws IllegalAccessException, InvocationTargetException { |
| if (myStoreAsClient) { |
| component.putClientProperty(INTRO_PREFIX + getName(), value); |
| } |
| else { |
| myWriteMethod.setAccessible(true); |
| myWriteMethod.invoke(component.getDelegee(), value); |
| } |
| } |
| |
| /** |
| * Serializes (writes) propertie's value |
| * |
| * @param value property value which should be serialized. |
| * @param writer writer which should be used for serialization. It is assumed that |
| * before invocation of this method <code>writer</code> already has opened tag |
| * that corresponds to this property. You can just append some attributes |
| * here or add some subtags. |
| */ |
| public void write(@NotNull V value, XmlWriter writer) { |
| writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, value.toString()); |
| } |
| |
| @Override public boolean isModified(final RadComponent component) { |
| return component.isMarkedAsModified(this); |
| } |
| |
| @Override public void resetValue(RadComponent component) throws Exception { |
| final V defaultValue = getDefaultValue(component.getDelegee()); |
| invokeSetter(component, defaultValue); |
| markTopmostModified(component, false); |
| } |
| |
| public void importSnapshotValue(final SnapshotContext context, final JComponent component, final RadComponent radComponent) { |
| try { |
| //noinspection unchecked |
| V value = (V) myReadMethod.invoke(component, EMPTY_OBJECT_ARRAY); |
| V defaultValue = getDefaultValue(radComponent.getDelegee()); |
| if (!Comparing.equal(value, defaultValue)) { |
| setValue(radComponent, value); |
| } |
| } |
| catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| protected V getDefaultValue(final JComponent delegee) throws Exception { |
| if (myStoreAsClient) { |
| return null; |
| } |
| final Constructor constructor = delegee.getClass().getConstructor(ArrayUtil.EMPTY_CLASS_ARRAY); |
| constructor.setAccessible(true); |
| JComponent newComponent = (JComponent)constructor.newInstance(ArrayUtil.EMPTY_OBJECT_ARRAY); |
| //noinspection unchecked |
| return (V) myReadMethod.invoke(newComponent, EMPTY_OBJECT_ARRAY); |
| } |
| |
| @Override |
| public boolean appliesTo(final RadComponent component) { |
| @NonNls String name = getName(); |
| //noinspection SimplifiableIfStatement |
| if (name.equals(SwingProperties.PREFERRED_SIZE) || |
| name.equals(SwingProperties.MINIMUM_SIZE) || |
| name.equals(SwingProperties.MAXIMUM_SIZE)) { |
| // our own properties must be used instead |
| final RadContainer parent = component.getParent(); |
| return parent != null && !(parent.getLayoutManager() instanceof RadGridLayoutManager); |
| } |
| |
| // check if property is available in the JDK used by the module containing the component |
| final PsiManager psiManager = PsiManager.getInstance(component.getProject()); |
| final GlobalSearchScope scope = component.getModule().getModuleWithDependenciesAndLibrariesScope(true); |
| PsiClass componentClass = JavaPsiFacade.getInstance(psiManager.getProject()).findClass(component.getComponentClassName(), scope); |
| if (componentClass == null) return true; |
| final PsiMethod[] psiMethods = componentClass.findMethodsByName(myReadMethod.getName(), true); |
| for(PsiMethod method: psiMethods) { |
| if (!method.hasModifierProperty(PsiModifier.STATIC) && |
| method.getParameterList().getParametersCount() == 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |