892050078e97339bf5aa55bae0bb5b674f00f48b
[idea/community.git] / java / compiler / forms-compiler / src / com / intellij / uiDesigner / lw / LwComponent.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.uiDesigner.lw;
17
18 import com.intellij.uiDesigner.UIFormXmlConstants;
19 import com.intellij.uiDesigner.core.GridConstraints;
20 import org.jdom.Element;
21
22 import java.awt.*;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27
28 /**
29  * @author Anton Katilin
30  * @author Vladimir Kondratyev
31  */
32 public abstract class LwComponent implements IComponent{
33   /**
34    *  Component's ID. Cannot be null.
35    */
36   private String myId;
37   /**
38    * may be null
39    */
40   private String myBinding;
41   /**
42    * Component class
43    */
44   private final String myClassName;
45   /**
46    * Parent LwContainer. This field is always not <code>null</code>
47    * is the component is in hierarchy. But the root of hierarchy
48    * has <code>null</code> parent indeed.
49    */
50   private LwContainer myParent;
51   /**
52    * never <code>null</code>
53    */
54   private final GridConstraints myConstraints;
55
56   private Object myCustomLayoutConstraints;
57   /**
58    * Bounds in XY layout
59    */
60   private final Rectangle myBounds;
61
62   private final HashMap myIntrospectedProperty2Value;
63   /**
64    * if class is unknown (cannot be loaded), properties tag is stored as is
65    */
66   private Element myErrorComponentProperties;
67   protected final HashMap myClientProperties;
68   protected final HashMap myDelegeeClientProperties;
69   private boolean myCustomCreate = false;
70   private boolean myDefaultBinding = false;
71
72   public LwComponent(final String className){
73     if (className == null){
74       throw new IllegalArgumentException("className cannot be null");
75     }
76     myBounds = new Rectangle();
77     myConstraints = new GridConstraints();
78     myIntrospectedProperty2Value = new LinkedHashMap();
79     myClassName = className;
80     myClientProperties = new LinkedHashMap();
81     myDelegeeClientProperties = new LinkedHashMap();
82   }
83
84   public final String getId() {
85     return myId;
86   }
87
88   public final void setId(final String id){
89     if (id == null) {
90       throw new IllegalArgumentException("id cannot be null");
91     }
92     myId = id;
93   }
94
95   public final String getBinding(){
96     return myBinding;
97   }
98
99   public final void setBinding(final String binding){
100     myBinding = binding;
101   }
102
103   public final Object getCustomLayoutConstraints(){
104     return myCustomLayoutConstraints;
105   }
106
107   public final void setCustomLayoutConstraints(final Object customLayoutConstraints){
108     myCustomLayoutConstraints = customLayoutConstraints;
109   }
110
111   /**
112    * @return never null
113    */
114   public final String getComponentClassName(){
115     return myClassName;
116   }
117
118   public IProperty[] getModifiedProperties() {
119     return getAssignedIntrospectedProperties();
120   }
121
122   /**
123    * @return component's constraints in XY layout. This method rever
124    * returns <code>null</code>.
125    */
126   public final Rectangle getBounds(){
127     return (Rectangle)myBounds.clone();
128   }
129
130   /**
131    * @return component's constraints in GridLayoutManager. This method never
132    * returns <code>null</code>.
133    */
134   public final GridConstraints getConstraints(){
135     return myConstraints;
136   }
137
138   public boolean isCustomCreate() {
139     return myCustomCreate;
140   }
141
142   public boolean isDefaultBinding() {
143     return myDefaultBinding;
144   }
145
146   public boolean accept(ComponentVisitor visitor) {
147     return visitor.visit(this);
148   }
149
150   public boolean areChildrenExclusive() {
151     return false;
152   }
153
154   public final LwContainer getParent(){
155     return myParent;
156   }
157
158   public IContainer getParentContainer() {
159     return myParent;
160   }
161
162   protected final void setParent(final LwContainer parent){
163     myParent = parent;
164   }
165
166   public final void setBounds(final Rectangle bounds) {
167     myBounds.setBounds(bounds);
168   }
169
170   public final Object getPropertyValue(final LwIntrospectedProperty property){
171     return myIntrospectedProperty2Value.get(property);
172   }
173
174   public final void setPropertyValue(final LwIntrospectedProperty property, final Object value){
175     myIntrospectedProperty2Value.put(property, value);
176   }
177
178   /**
179    * @return <code>null</code> only if component class is not valid.
180    * Class validation is performed with {@link com.intellij.uiDesigner.compiler.Utils#validateJComponentClass(ClassLoader,String,boolean)}
181    */
182   public final Element getErrorComponentProperties(){
183     return myErrorComponentProperties;
184   }
185
186   public final LwIntrospectedProperty[] getAssignedIntrospectedProperties() {
187     final LwIntrospectedProperty[] properties = new LwIntrospectedProperty[myIntrospectedProperty2Value.size()];
188     final Iterator iterator = myIntrospectedProperty2Value.keySet().iterator();
189     //noinspection ForLoopThatDoesntUseLoopVariable
190     for (int i=0; iterator.hasNext(); i++) {
191       properties[i] = (LwIntrospectedProperty)iterator.next();
192     }
193     return properties;
194   }
195
196   /**
197    * 'id' is required attribute
198    * 'binding' is optional attribute
199    */
200   protected final void readBase(final Element element) {
201     setId(LwXmlReader.getRequiredString(element, UIFormXmlConstants.ATTRIBUTE_ID));
202     setBinding(element.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_BINDING));
203     myCustomCreate = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_CUSTOM_CREATE, false);
204     myDefaultBinding = LwXmlReader.getOptionalBoolean(element, UIFormXmlConstants.ATTRIBUTE_DEFAULT_BINDING, false);
205   }
206
207   /**
208    * 'properties' is not required subtag
209    * @param provider can be null if no properties should be read 
210    */
211   protected final void readProperties(final Element element, final PropertiesProvider provider) {
212     if (provider == null) {
213       // do not read properties 
214       return;
215     }
216
217     Element propertiesElement = LwXmlReader.getChild(element, UIFormXmlConstants.ELEMENT_PROPERTIES);
218     if(propertiesElement == null){
219       propertiesElement = new Element(UIFormXmlConstants.ELEMENT_PROPERTIES, element.getNamespace());
220     }
221
222     final HashMap name2property = provider.getLwProperties(getComponentClassName());
223     if (name2property == null) {
224       myErrorComponentProperties = (Element)propertiesElement.clone();
225       return;
226     }
227
228     final List propertyElements = propertiesElement.getChildren();
229     for (int i = 0; i < propertyElements.size(); i++) {
230       final Element t = (Element)propertyElements.get(i);
231       final String name = t.getName();
232       final LwIntrospectedProperty property = (LwIntrospectedProperty)name2property.get(name);
233       if (property == null){
234         continue;
235       }
236       try {
237         final Object value = property.read(t);
238         setPropertyValue(property, value);
239       }
240       catch (final Exception exc) {
241         // Skip non readable properties
242       }
243     }
244
245     readClientProperties(element);
246   }
247
248   private void readClientProperties(final Element element) {
249     Element propertiesElement = LwXmlReader.getChild(element, UIFormXmlConstants.ELEMENT_CLIENT_PROPERTIES);
250     if (propertiesElement == null) return;
251     final List clientPropertyList = propertiesElement.getChildren();
252     for(int i=0; i<clientPropertyList.size(); i++) {
253       final Element prop = (Element) clientPropertyList.get(i);
254       final String propName = prop.getName();
255       final String className = LwXmlReader.getRequiredString(prop, UIFormXmlConstants.ATTRIBUTE_CLASS);
256
257       LwIntrospectedProperty lwProp;
258       if (className.equals(Integer.class.getName())) {
259         lwProp = new LwIntroIntProperty(propName);
260       }
261       else if (className.equals(Boolean.class.getName())) {
262         lwProp = new LwIntroBooleanProperty(propName);
263       }
264       else if (className.equals(Double.class.getName())) {
265         lwProp = new LwIntroPrimitiveTypeProperty(propName, Double.class);
266       }
267       else {
268         Class propClass;
269         try {
270           propClass = Class.forName(className);
271         }
272         catch (ClassNotFoundException e) {
273           continue;
274         }
275         lwProp = CompiledClassPropertiesProvider.propertyFromClass(propClass, propName);
276       }
277
278       if (lwProp != null) {
279         final Object value;
280         try {
281           value = lwProp.read(prop);
282         }
283         catch (Exception e) {
284           continue;
285         }
286         myDelegeeClientProperties.put(propName, value);
287       }
288     }
289   }
290
291   /**
292    * Delegates reading of constraints to the parent container
293    */
294   protected final void readConstraints(final Element element){
295     final LwContainer parent = getParent();
296     if(parent == null){
297       throw new IllegalStateException("component must be in LW tree: "+this);
298     }
299     parent.readConstraintsForChild(element, this);
300   }
301
302   /**
303    * @param provider can be null if no component classes should not be created
304    */
305   public abstract void read(Element element, PropertiesProvider provider) throws Exception;
306
307   /**
308    * @see javax.swing.JComponent#getClientProperty(Object)
309    */
310   public final Object getClientProperty(final Object key){
311     if (key == null) {
312       throw new IllegalArgumentException("key cannot be null");
313     }
314     return myClientProperties.get(key);
315   }
316
317   /**
318    * @see javax.swing.JComponent#putClientProperty(Object, Object)
319    */
320   public final void putClientProperty(final Object key, final Object value){
321     if (key == null) {
322       throw new IllegalArgumentException("key cannot be null");
323     }
324     myClientProperties.put(key, value);
325   }
326
327   public HashMap getDelegeeClientProperties() {
328     return myDelegeeClientProperties;
329   }
330 }