blob: 99c5bfd22ee067a23a855bb68d7e9ec267f2fad2 [file] [log] [blame]
/*
* 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;
}
}