1 // Copyright Hugh Perkins 2009
2 // hughperkins@gmail.com http://manageddreams.com
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by the
6 // Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 //  more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program in the file licence.txt; if not, write to the
16 // Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
17 // 1307 USA
18 // You can find the licence also on the web at:
19 // http://www.opensource.org/licenses/gpl-license.php
20 //
21 // ======================================================================================
22 //
23 
24 package hughai.ui;
25 
26 import java.util.*;
27 import java.util.Map;
28 import java.awt.Component;
29 import java.awt.GridLayout;
30 import java.awt.event.ActionEvent;
31 import java.awt.event.ActionListener;
32 import java.lang.annotation.*;
33 import java.lang.reflect.*;
34 
35 import javax.swing.*;
36 import javax.swing.event.ListSelectionEvent;
37 import javax.swing.event.ListSelectionListener;
38 
39 import com.springrts.ai.*;
40 import com.springrts.ai.oo.clb.*;
41 
42 import hughai.*;
43 import hughai.EnemyTracker.EnemyAdapter;
44 import hughai.basictypes.*;
45 import hughai.building.Workflows;
46 import hughai.mapping.*;
47 import hughai.packcoordinators.*;
48 import hughai.unitdata.*;
49 import hughai.unitdata.UnitController.UnitAdapter;
50 import hughai.utils.*;
51 import hughai.utils.ReflectionHelper.CustomClass;
52 import hughai.utils.ReflectionHelper.Exclude;
53 import hughai.utils.ReflectionHelper.ListTypeInfo;
54 
55 public class UiReflectionHelper {
56    PlayerObjects playerObjects;
57 
UiReflectionHelper( PlayerObjects playerObjects )58    public UiReflectionHelper( PlayerObjects playerObjects ) {
59       this.playerObjects = playerObjects;
60    }
61 
dispose()62    public void dispose() {
63 
64    }
65 
objectToEditComponent( Object object, Annotation[] annotations )66    JComponent objectToEditComponent( Object object, Annotation[] annotations ) {
67       Class<?> objectClass = object.getClass();
68       if( objectClass == String.class ) {
69          JTextField textField = new JTextField();
70          textField.setText( "" + object );
71          return textField;
72       }
73       if( objectClass == boolean.class ) {
74          JCheckBox checkBox = new JCheckBox();
75          checkBox.setSelected( (Boolean)object );
76          return checkBox;
77       }
78       if( objectClass == float.class ) {
79          JTextField textField = new JTextField();
80          textField.setText( "" + object );
81          return textField;
82       }
83       if( objectClass == int.class ) {
84          JTextField textField = new JTextField();
85          textField.setText( "" + object );
86          return textField;
87       }
88       if( List.class.isAssignableFrom( objectClass ) ) {
89 
90       }
91       //      boolean foundCustomClassAttribute = false;
92       //      for( Annotation annotation : annotations ) {
93       //         if( CustomClass.class.isAssignableFrom( annotation.getClass() ) ) {
94       //            foundCustomClassAttribute = true;
95       //         }
96       //      }
97       //      if( foundCustomClassAttribute ) {
98       // then, we put each field into its own column in a row?
99       // and if one of them is a  list?
100       for( Field field : objectClass.getDeclaredFields() ) {
101 
102       }
103       //      }
104 
105       throw new RuntimeException("UiReflectionhelper: unhandled type: " + objectClass.getSimpleName() );
106    }
107 
108    public enum DisplayContext {
109       detailed,
110       listrow,
111       brief
112    }
113 
114    // ok, so if we call with a primitive, it's easy.
115    // if we call with a class, then we want either:
116    //   fieldone: valueone
117    //   fieldtwo: valuetwo
118    //   fieldthree: valuethree
119    // or:
120    //   fieldone    fieldtwo    fieldthree
121    //   valueone    valuetwo    valuethree
122    // maybe the first is better?
123    // next, if we call with a list, then we want something like:
124    //   valueone
125    //   valuetwo
126    //   valuethree
127    // what if it is a list of classes?  then something like:
128    //   fieldone    fieldtwo   fieldthree
129    //   itemonev1   itemonev2  itemonev3
130    //   itemtwov1   itemtwov2  itemtwov3
131    //
132    // if we call with a class with a list in, then something like:
133    //    fieldone: valueone
134    //    fieldtwo: valuetwo
135    //    fieldthree: valuethree
136    //    listfield: [button here to see details]
137    // or maybe:
138    //    fieldone: valueone
139    //    fieldtwo: valuetwo
140    //    fieldthree: valuethree
141    //    listfield:
142    //    valueone
143    //    valuetwo
144    //    valuethree
145    // or:
146    //    fieldone: valueone
147    //    fieldtwo: valuetwo
148    //    fieldthree: valuethree
149    //    listfield:  valueone
150    //                valuetwo
151    //                valuethree
152    // first option: button to see details, is easiest
153    //
154    // what if list of classes, and the class contains a list?
155    // maybe as before for a list of classes, with the button added?
156    //
157    // ok, so detailed can be true or false:
158    // if true, then class fields will be listed vertically, and list in full
159    // if false, then lists and classes will just become a button?
160    // if true, and classasrowtable is true, then a class will be horizontal,
161    // as a table row, otherwise vertically
162    //
163    // so much for layout... what about behavior when canModify is true?
164    // - probably need a button 'apply', 'revert' for each dialog and sub-dialog, ie for classes, maybe also for lists?
165    //   - apply and revert applies only to that dialog, not to modifications to children dialogs
166    // - for lists, need button for each row, to delete it, and a button to add a new row
167    // - for primitive types, nothing is applied until parent class or list applies it?
168    // - maybe 'apply' and 'revert' apply at the level of each custom class?
objectToDisplayComponent( final Object object, final Annotation[] annotations, DisplayContext displayContext, final boolean canModify )169    public JComponent objectToDisplayComponent( final Object object, final Annotation[] annotations, DisplayContext displayContext, final boolean canModify ) {
170       try {
171       if( object == null ) {
172          return new JLabel("<null>");
173       }
174 
175       final Class<?> objectClass = object.getClass();
176       if( objectClass == String.class ) {
177          if( canModify ) {
178             JTextField textField = new JTextField();
179             textField.setText( "" + object );
180             return textField;
181          } else {
182             JLabel textField = new JLabel();
183             textField.setText( "" + object );
184             return textField;
185          }
186       }
187       if( objectClass.equals( Float.class ) ) {
188          if( canModify ) {
189             JTextField textField = new JTextField();
190             textField.setText( "" + object );
191             return textField;
192          } else {
193             JLabel textField = new JLabel();
194             textField.setText( "" + object );
195             return textField;
196          }
197       }
198       if( objectClass.equals( Integer.class ) ) {
199          if( canModify ) {
200             JTextField textField = new JTextField();
201             textField.setText( "" + object );
202             return textField;
203          } else {
204             JLabel textField = new JLabel();
205             textField.setText( "" + object );
206             return textField;
207          }
208       }
209       if( objectClass.equals( Boolean.class ) ) {
210          JCheckBox checkbox = new JCheckBox();
211          checkbox.setEnabled( canModify );
212          checkbox.setSelected( (Boolean )object );
213          return checkbox;
214       }
215       if( List.class.isAssignableFrom( objectClass ) ) {
216          Class<?> listItemClass = getListItemClass( annotations );
217          if( listItemClass == null ) {
218             throw new RuntimeException("Missing ListTypeInfo annotation on list" );
219          }
220 
221          if( displayContext.equals( DisplayContext.brief )
222                || displayContext.equals( DisplayContext.listrow ) ) { // just return a button
223             final JButton button = new JButton( "..." );
224             button.addActionListener( new ActionListener() {
225                @Override
226                public void actionPerformed( ActionEvent e ) {
227                   System.out.println("creating child dialog...");
228                   JDialog childDialog = createDialog( button, "List", true );
229                   JComponent childcomponents = objectToDisplayComponent(object, annotations, DisplayContext.detailed, canModify );
230                   childDialog.add( childcomponents );
231                   childDialog.setSize( 300, 300 );
232                   childDialog.setVisible( true );
233                }
234             } );
235             return button;
236          }
237 
238          // otherwise show as a table
239          List objectAsList = (List)object;
240          GridLayout gridLayout = new GridLayout(0,1);
241          JPanel panel = new JPanel( gridLayout );
242 
243          JComponent headers = listToHeaders( listItemClass );
244          if( headers != null ) {
245             addGridLayoutRow( gridLayout );
246             panel.add( headers );
247          }
248 
249          for( Object item : objectAsList ) {
250             JComponent childcomponent = objectToDisplayComponent( item,
251                   annotations, DisplayContext.listrow, canModify );
252             addGridLayoutRow( gridLayout );
253             panel.add( childcomponent );
254          }
255          return panel;
256       }
257       //      boolean foundCustomClassAttribute = false;
258       //      for( Annotation annotation : annotations ) {
259       //         if( CustomClass.class.isAssignableFrom( annotation.getClass() ) ) {
260       //            foundCustomClassAttribute = true;
261       //         }
262       //      }
263       //      if( foundCustomClassAttribute ) {
264       if( displayContext.equals( DisplayContext.brief ) ) { // just return a button
265 //         JPanel panel = new JPanel( new GridLayout( 1, 1 ));
266 
267 //         final JLabel label = new JLabel( objectClass.getSimpleName() );
268 
269          final JButton button = new JButton( "..." );
270          button.setSize( 50, 50 );
271 
272          button.addActionListener( new ActionListener() {
273             @Override
274             public void actionPerformed( ActionEvent e ) {
275                System.out.println("creating child dialog...");
276                JDialog childDialog = createDialog( button, objectClass.getSimpleName(), true );
277                JComponent childcomponents = objectToDisplayComponent(object, new Annotation[0], DisplayContext.detailed, canModify );
278                childDialog.add( childcomponents );
279                childDialog.setSize( 300, 300 );
280                childDialog.setVisible( true );
281             }
282          } );
283 //         panel.add( label );
284 //         panel.add( button );
285          return button;
286 //         return panel;
287       }
288 
289       if( displayContext.equals( DisplayContext.listrow ) ) { // put each field into a column
290          GridLayout gridLayout = new GridLayout(1,0);
291          JPanel panel = new JPanel( gridLayout );
292          for( Field field : objectClass.getDeclaredFields() ) {
293             JComponent childcomponent = objectToDisplayComponent( field.get( object ), field.getAnnotations(), DisplayContext.brief, canModify );
294             gridLayout.setColumns( gridLayout.getColumns() + 1 );
295             panel.add( childcomponent );
296          }
297          return panel;
298       } else {
299          // then, we put each field into its own row
300          GridLayout gridLayout = new GridLayout(0,2);
301          JPanel panel = new JPanel( gridLayout );
302          for( Field field : objectClass.getDeclaredFields() ) {
303             JLabel label = new JLabel( field.getName() );
304             JComponent childcomponent = objectToDisplayComponent( field.get( object ), field.getAnnotations(), DisplayContext.brief, canModify );
305 
306             gridLayout.setRows( gridLayout.getRows() + 1 );
307 
308             panel.add(  label );
309             panel.add( childcomponent );
310          }
311          return panel;
312       }
313       } catch( Exception e ) {
314          e.printStackTrace();
315          throw new RuntimeException( e );
316       }
317 
318       //      }
319 
320 //      throw new RuntimeException("UiReflectionhelper: unhandled type: " + objectClass.getSimpleName() );
321    }
322 
listToHeaders( Class<?> listClass )323    JComponent listToHeaders( Class<?> listClass ) {
324       if( listClass.isPrimitive() ) {
325          return null;
326       }
327 
328       if( listClass.equals( String.class ) ) {
329          return null;
330       }
331 
332       GridLayout gridLayout = new GridLayout( 1, 0 );
333       JPanel panel = new JPanel( gridLayout );
334 
335       // assume custom class, for now
336       for( Field field : listClass.getDeclaredFields() ) {
337          if( isExcluded( field ) ) {
338             continue;
339          }
340 
341          JLabel label = new JLabel( field.getName() );
342          addGridLayoutColumn( gridLayout );
343          panel.add( label );
344       }
345       return panel;
346    }
347 
addGridLayoutRow( GridLayout gridLayout )348    void addGridLayoutRow( GridLayout gridLayout ) {
349       gridLayout.setRows( gridLayout.getRows() + 1 );
350    }
351 
addGridLayoutColumn( GridLayout gridLayout )352    void addGridLayoutColumn( GridLayout gridLayout ) {
353       gridLayout.setColumns( gridLayout.getColumns() + 1 );
354    }
355 
isExcluded(Annotation[] annotations )356    boolean isExcluded(Annotation[] annotations ) {
357       for( Annotation annotation : annotations ) {
358          if( Exclude.class.isAssignableFrom( annotation.getClass() ) ) {
359             return true;
360          }
361       }
362       return false;
363    }
364 
isExcluded( Field field )365    boolean isExcluded( Field field ) {
366       return isExcluded( field.getAnnotations() ); // not that efficient, but do we care?
367    }
368 
getListItemClass( Annotation[] annotations )369    Class<?> getListItemClass( Annotation[] annotations ) {
370       for( Annotation annotation : annotations ) {
371          if( ListTypeInfo.class.isAssignableFrom( annotation.getClass() ) ) {
372             ListTypeInfo listTypeInfo = (ListTypeInfo)annotation;
373             return listTypeInfo.value();
374          }
375       }
376       return null;
377    }
378 
createDialog( Component lockedtoparentofthiscomponent, String title, boolean modal )379    JDialog createDialog( Component lockedtoparentofthiscomponent, String title, boolean modal ) {
380       if( JFrame.class.isAssignableFrom( lockedtoparentofthiscomponent.getClass() ) ) {
381          JDialog dialog = new JDialog( (JFrame)lockedtoparentofthiscomponent,
382                title, modal );
383          return dialog;
384       }
385       if( JDialog.class.isAssignableFrom( lockedtoparentofthiscomponent.getClass() ) ) {
386          JDialog dialog = new JDialog( (JDialog)lockedtoparentofthiscomponent,
387                title, modal );
388          return dialog;
389       }
390       if( lockedtoparentofthiscomponent == null ) {
391          throw new RuntimeException("UiReflectionHelper: couldn't find parent frame or dialog" );
392       }
393       return createDialog( lockedtoparentofthiscomponent.getParent(), title, modal );
394    }
395 
396 //   JComponent objectToTableHeaders( Object object, Annotation[] annotations ) {
397 //      return null; // placeholder
398 //   }
399 }
400