Kava Pava Gava Tava Nava Zava Java

          everything about Java
          隨筆 - 15, 文章 - 0, 評論 - 1, 引用 - 0
          數據加載中……

          Exploring Vaadin (3) 閱讀 com.vaadin.ui.Form 源代碼

          Form 是 vaadin 用戶界面的一個組成部分,下面結合其源代碼(ver 6.1.5)進行分析。

          public class Form extends AbstractField implements Item.Editor, Buffered, Item,
                  Validatable {

          Form 實現了 vaadin 數據模型的上述接口,其中最重要的應該是 Item.Editor,這個接口代表 Form 是用來編輯 Item 的。Item 可以是一個 pojo,一個數據表中的一行等等。總之,Item 是 Property 的集合。具體請看我的第一篇 Data 部分。

              private Object propertyValue;
              private Layout layout;
              private Item itemDatasource;
              private final LinkedList<Object> propertyIds = new LinkedList<Object>();


          保存加入的 id,僅在addItemProperty(Object id, Property property) 和 addField(Object propertyId, Field field) 中被添加。在很多方法中被遍歷。與ownProperties的不同之處就在于多了一個方法,即addField中也會添加。

              private Buffered.SourceException currentBufferedSourceException = null;

          這個currentBufferedSourceException 在commit()以及discard()時被設置或者清除,用來保存各個field.commit()/field.discard()過程中捕捉到的Buffered.SourceException并且被拋出。在getErrorMessage()中被返回。

              private boolean writeThrough = true;
              private boolean readThrough = true;
              private final HashMap<Object, Field> fields = new HashMap<Object, Field>();
              private final HashMap<Object, Property> ownProperties = new HashMap<Object, Property>();


          所有通過addItemProperty(Object id, Property property) 加入的property 被按照 id 加入到ownProperties 中。在 getItemProperty 時僅僅作為第三選擇,在沒有對應的Field時才被返回。在 removeItemProperty 時也被除去。

              private FormFieldFactory fieldFactory;
              private Collection<Object> visibleItemProperties;
              private Layout formFooter;
              private boolean validationVisibleOnCommit = true;
              // special handling for gridlayout; remember initial cursor pos
              private int gridlayoutCursorX = -1;
              private int gridlayoutCursorY = -1;


          上面是 members


              private final ValueChangeListener fieldValueChangeListener = new ValueChangeListener() {
                  public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
                      requestRepaint();
                  }
              };


          所有的 field 都會被加上這個監聽器,這樣當任何一個 field 的值發生變化時都會使 Form 重新繪制。

              public ErrorMessage getErrorMessage() {

          考慮三個 error 來源:
           - getComponentError() 這個是從 AbstractComponent 繼承下來的,可以被設置, 設置時會觸發 Component.ErrorEvent。
           - validationError,來自各個Field 的getErrorMessage(),但只抓第一個。詳情見下面。
           - currentBufferedSourceException,上一次commit()或者discard()收集到的Buffered.SourceException,見上面介紹。

          得到 validationError:如果 isValidationVisible() (默認否),那么對 propertyIds 遍歷,從 fields Map中得到各個 Field,如果 field.getErrorMessage() 不為空,則拋出 Validator.InvalidValueException。這個Validator.InvalidValueException 可能就是從field.getErrorMessage() 得到的那個。但是如果消息未空或者有其他問題,建立一個InvalidValueException,以 Field.getCaption() 作為內容。注意,只要一個Field得到 InvalidValueException,其他Field就不再遍歷了。

          然后,考慮上面說到的三個源。如果都為空,返回空。否則拋出 CompositeErrorMessage,包含這三個錯誤源。

              public void commit() throws Buffered.SourceException {


          如果isInvalidCommitted()為否,并且isValid()為否,進行 validate(),可能拋出異常。

          如果沒有進行 validate() 或者 validate() 通過,則遍歷 propertyIds,從 fields 里面按照 id 取出各個 Field,除非 isReadOnly(),否則調用 commit()。捕捉可能拋出的 Buffered.SourceException, 先加入到一個問題列表中。

          最后,如果問題列表為空,清空currentBufferedSourceException ,requestRepaint(),順利返回。否則,create 一個新的 Buffered.SourceException,把收集到的 Exception 當作 cause,然后設為currentBufferedSourceException ,要求 requestRepaint(),并拋出剛剛生成的 currentBufferedSourceException。

              public void discard() throws Buffered.SourceException {

          過程和commit()基本一致,只是調用各個field.commit()換成了調用 field.discard()。同樣,也捕捉處理Buffered.SourceException,放在currentBufferedSourceException中。

              public boolean isModified() {

          遍歷 propertyIds,調用各個 Field 的 isModified(),任何為真則返回真。

              public void setReadThrough(boolean readThrough) {

          如果確實有變化,遍歷 propertyIds,調用各個 Field 的 setReadThrough()。

              public void setWriteThrough(boolean writeThrough) throws SourceException,

          同上處理。

              public boolean addItemProperty(Object id, Property property) {

          如果 propertyIds 已經包括 id,也就是說以前加入過,直接返回 false。

          否則,通過 fieldFactory 建立Field。配置Field,包括setPropertyDataSource,然后從id.toString()經過長度,首字母大小寫調整,設置為Caption。最后調用 addField(id,field)加入 Field。

              public void addField(Object propertyId, Field field) {

          將要加入的field 加入到 fields,給要加入的 field 加上fieldValueChangeListener以便在 field 的值改變時重新繪制Form,如果propertyIds不包含 propertyId,加入(這是被外部調用的時候發生的)。調整field的 readthrough / writethrough / immediate 和 Form 一致,最后將 field 加入本 Form 的 Layout。在加入 Layout 時,如果是 CustomLayout,以propertyId.toString()為位置標記。否則只是簡單順次加入。

          這里本人對 vaadin 的做法有保留。propertyId.toString()被作為 Field的Caption,又被作為很多 Map 的key,又被作為 CustomLayout 的位置標記,是不是擔當的角色太多了?從內部結構標識到給用戶的Caption, 會給國際化帶來麻煩嗎?等我進一步深入探索使用,才能得到結論。

              public Property getItemProperty(Object id) {

          依照下面次序返回:
          如果 fields.get(id) 得到Field,再從field.getPropertyDataSource() 得到 property,則返回 property
          否則,返回 field,因為 field 就是一個 property
          再否則,返回 ownProperties.get(id)
          關于 ownProperties,詳見上面說明

              public Field getField(Object propertyId) {
                  return fields.get(propertyId);
              }


              public Collection getItemPropertyIds() {
                  return Collections.unmodifiableCollection(propertyIds);
              }


          以上兩條代碼簡單明了。Collections.unmodifiableCollection 不錯,今后可以多用。


              public boolean removeItemProperty(Object id) {

          從 ownProperties, fields, propertyIds, layout 中去掉 id 相對應的元素。這里面從ownProperties去掉后先判斷是不是有對應的field,如果有,才從fields, propertyIds, layout 中去掉。為什么這么寫?這是因為如果有 propertyId,就一定有相應的 field,加入到 layout?看代碼,應該是這樣吧。

              public boolean removeAllProperties() {

          對所有 propertyIds 依次調用 removeItemProperty。

              public Item getItemDataSource()

          就是簡單的getter。


              public void setItemDataSource(Item newDataSource) {
                  setItemDataSource(newDataSource, newDataSource != null ? newDataSource
                          .getItemPropertyIds() : null);
              }

          看下面的實現。

              public void setItemDataSource(Item newDataSource, Collection propertyIds) {


          首先 removeAllProperties(),設置 itemDatasource,然后遍歷參數 propertyIds,從itemDatasource取出相應的 property,通過 fieldFactory 得到 Field,設置field.setPropertyDataSource 為所取出的property,調用 addField 來添加 Field。

          奇怪的是這里為什么沒有設置 Field 的 Caption? 為什么不是調用 addItemProperty 來處理,而是要寫重復代碼?很奇怪。原來默認的DefaultFieldFactory里面在 createField 時確實設置 Caption的。createField 調用 createCaptionByPropertyId 來得到Caption,而createCaptionByPropertyId 進行格式處理,將 "firstName" 便成為 "First Name"。這個做法只能是一個默認的方便的做法。看來 FieldFactory確實要自己去寫的。可是這樣看來,addItemProperty 在 createField 之后又去操作 field.setCaption() 和進行格式化是多余了。注意 addItemProperty 是以 this,也就是這個 Form 作為Item參數去調用 createField的,而setItemDataSource 是以得到的 itemDataSource,也就是 newDataSource 進行的。這樣就明白了。addItemProperty 是添加一個不在 itemDataSource中的一個獨立的 property。這兩個函數,addItemProperty 和 setItemDataSource,要說addItemProperty 的名字起得有點誤導。應該是 addFormProperty 或者 addStandaloneProperty 更好些嗎?看來“正常”使用的時候,就是用來和一個Item綁定的時候,是應該不會調用addFormProperty 的。除非,除了Item的那些property之外,還要加其他property。


              public Layout getLayout() {

          就是 getter

              public void setLayout(Layout newLayout) {


          用新的 layout 替換舊的。如果有舊的 layout,把所有的 field 一一從舊 layout 中去掉,再加入到新的里面。

              public Select replaceWithSelect(Object propertyId, Object[] values,
                      Object[] descriptions) {


          這個沒有細看,用到時候再說吧。應該用 FieldFactory 來解決問題。

              @Override
              public void attach() {
                  super.attach();
                  layout.attach();
              }

              @Override
              public void detach() {
                  super.detach();
                  layout.detach();
              }


          這兩個比較簡單。

              @Override
              public boolean isValid() {


          依次調用 propertyIds 對應的 field,再調用 isValid(),進行與運算。最后與 super.isValid() 進行與運算。

              @Override

          與上面順序相反,先調用 super.validate(),再順次調用 propertyIds 對應的 field,再調用 validate()

              public boolean isInvalidAllowed() {
                  return true;
              }

              public void setInvalidAllowed(boolean invalidValueAllowed) 沒有實現,直接拋出異常

              public void setReadOnly(boolean readOnly) {

          依次調用 super 以及 propertyIds 對應的各個 field 的相應方法

              public void setFormFieldFactory(FormFieldFactory fieldFactory) {
              public FormFieldFactory getFormFieldFactory() {


          就是setter 和 getter

              public Class getType() {
                  if (getPropertyDataSource() != null) {
                      return getPropertyDataSource().getType();
                  }
                  return Object.class;
              }

          很簡單。。。

              protected void setInternalValue(Object newValue) {

          首先 super.setInternalValue(newValue),propertyValue = newValue; 然后看是不是和舊的 propertyValue 不同。如果是的話,則調用受保護方法 setFormDataSource(newValue, getVisibleItemProperties())。setFormDataSource 得到 Item (如果 newValue 不是 Item,就生成BeanItem),調用setItemDataSource。visibleItemProperties 不知什么用途。但是總之,看起來 setInternalValue,setVisibleItemProperties是一起用的。注釋說這個方法是在Form被當作 Field使用時用的。

              private Field getFirstField() {

          得到 propertyIds 第一個元素對應的 Field

              protected void setFormDataSource(Object data, Collection properties) {


          如果data是一個Item,用之,否則生成BeanItem。調用setItemDataSource

              public Collection getVisibleItemProperties() {
                  return visibleItemProperties;
              }


          只是getter

              public void setVisibleItemProperties(Collection visibleProperties) {

          設置 visibleItemProperties,然后調用AbstractField的getValue,如果為空則還是用itemDataSource,否則用getValue,去調用 setFormDataSource(value, getVisibleItemProperties());

              public void setVisibleItemProperties(Object[] visibleProperties) {

          將array轉成collection然后設置

              public void focus() {

          focus 在第一個 field

              public void setTabIndex(int tabIndex) {

              public void setImmediate(boolean immediate) {


          依次調用各個field

              protected boolean isEmpty() {

          依次判斷各個field

              public void addValidator(Validator validator) {

          不支持。

              public Layout getFooter() {

          簡單,不再詳述。

              public void setFooter(Layout newFormFooter) {

          簡單,不再詳述。

              public void setEnabled(boolean enabled) {

          調用 super.setEnabled,奇怪,似乎沒有調用各個 field 的setEnabled()。

          posted on 2009-12-23 16:10 bing 閱讀(740) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 东台市| 高唐县| 白玉县| 通海县| 武功县| 稻城县| 如皋市| 甘谷县| 徐汇区| 香港| 达孜县| 舒兰市| 招远市| 灵武市| 巍山| 嘉定区| 阿荣旗| 长乐市| 文昌市| 保山市| 德兴市| 上高县| 奉新县| 抚宁县| 台安县| 杭锦后旗| 泰顺县| 鄂伦春自治旗| 安泽县| 施秉县| 永新县| 聊城市| 吴桥县| 绥宁县| 锦屏县| 中西区| 正安县| 基隆市| 科尔| 隆化县| 邯郸县|