View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2007 Tim Pizey
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     Tim Pizey <timp At paneris.org>
42   *     http://paneris.org/~timp
43   */
44  package org.melati.poem;
45  
46  import java.lang.reflect.Method;
47  import java.util.Enumeration;
48  
49  import org.melati.poem.util.ClassUtils;
50  import org.melati.poem.util.StringUtils;
51  
52  /**
53   * A factory for persisting pojos and recreating them.
54   * 
55   * @author timp
56   * @since 14 Jun 2007
57   * 
58   */
59  public final class PersistentFactory {
60  
61    /**
62     * Disallow instantiation.
63     */
64    private PersistentFactory() {
65    }
66  
67    /**
68     * @param db
69     *          database to look in
70     * @param pojo
71     *          the instance to create a Persistent from.
72     * @return A new or existing persisted Persistent
73     */
74    public static Persistent fromInstance(Database db, Object pojo) {
75      System.err.println("fromInstance - looking for " + pojo.getClass().getName());
76      Table<?> table = null;
77      Persistent p = null;
78      if (pojo instanceof Persistent) {
79        if (((Persistent)pojo).troid() != null) { 
80          return (Persistent)pojo;
81        } else {
82          p = populatedPersistent(((Persistent)pojo).getTable(), pojo);
83          p.makePersistent();
84          return p;
85        }
86      } else
87        table = TableFactory.fromInstance(db, pojo);
88      p = populatedPersistent(table, pojo);
89      Enumeration<?> candidates = table.selection(p);
90      while (candidates.hasMoreElements()) {
91        Persistent candidate = (Persistent)candidates.nextElement();
92        if (commonFieldsEqual(p, candidate)) { 
93          p = candidate;
94          System.err.println("Candidate: " + p);
95          break;
96        } else 
97          System.err.println("Non Candidate: " + p);
98      }
99      if (p.getTroid() == null) {
100       System.err.println("About to persist : " + p);
101       p.makePersistent();
102       System.err.println("Have persisted : " + p);
103     }
104     System.err.println("Returning : " + p);
105     return p;
106   }
107 
108   private static boolean commonFieldsEqual(Persistent criterion, Persistent candidate) {
109     Enumeration<Column<?>> cols = criterion.getTable().columns();
110     while (cols.hasMoreElements()) { 
111       Column<?> col = (Column<?>)cols.nextElement();
112       if (col.isTroidColumn()) 
113         continue;
114       if (col.getRaw(criterion) != null && 
115                !col.getRaw(criterion).equals(col.getRaw(candidate))) { 
116         System.err.println("Reject");
117         return false;
118       }
119     }
120     return true;
121   }
122 
123   /**
124    * @param table
125    *          the Table the persisted pojo is to be stored in
126    * @param pojo
127    *          the object to populate the Persistent from
128    * @return A floating Persistent with fields populated from the given pojo
129    */
130   public static Persistent populatedPersistent(Table<?> table, Object pojo) {
131     if (pojo instanceof Persistent) 
132       if (((Persistent)pojo).troid() != null)
133         return table.getObject(((Persistent)pojo).troid().intValue());
134       else 
135         return ((Persistent)pojo);
136     Persistent p = table.newPersistent();
137     Class<?> c = pojo.getClass();
138     Enumeration<Column<?>> columns = table.columns();
139     while (columns.hasMoreElements()) {
140       Column<?> col = (Column<?>)columns.nextElement();
141       if(col.isTroidColumn()) continue;
142       Method memberGetter;
143       Object raw;
144       try {
145         memberGetter = c.getMethod("get" + StringUtils.capitalised(col.getName()), 
146                    new Class[] {});
147       } catch (NoSuchMethodException e) {
148         try {
149           memberGetter = c.getMethod("is" + StringUtils.capitalised(col.getName()), 
150                                      new Class[] {});
151         } catch (NoSuchMethodException e1) {
152           throw new AppBugPoemException(
153                   "No getter available for field " + col.getName() + 
154                   " on object of class " + pojo.getClass().getName(), e1);
155         }
156       } 
157       try {
158         raw = memberGetter.invoke(pojo, new Object[] {});
159       } catch (Exception e) {
160         throw new AppBugPoemException(
161                 "Problem invoking getter on column  " + col.getName(), e);
162       }
163       if (raw != null) {
164         try {
165           if (col.getType() instanceof PersistentReferencePoemType) {
166             p.setCooked(col.getName(), 
167                         PersistentFactory.fromInstance(table.getDatabase(), raw));
168           } else {
169             p.setCooked(col.getName(), raw);
170           }
171         } catch (TypeMismatchPoemException e) {
172           throw new AppBugPoemException("Problem setting value of Column "
173                   + col.getName(), e);
174         }
175       }
176     }
177     return p;
178   }
179 
180   /**
181    * Reincarnate an object, populated from the store.
182    * 
183    * @param persistent
184    *          the Persistent to read from
185    * @param clazz
186    *          the class to create a new, populated instance of
187    * @return an instance of the given Class, populated from the given Persistent
188    * @throws NoSuchMethodException
189    */
190   public static Object from(Persistent persistent, Class<?> clazz)
191           throws NoSuchMethodException {
192     Object it;
193     try {
194       it = clazz.newInstance();
195     } catch (Exception e) {
196       throw new AppBugPoemException("Problem creating new instance of "
197               + clazz.getName(), e);
198     }
199     return populatedPojo(it, persistent);
200   }
201 
202   private static Object populatedPojo(Object pojo, Persistent persistent) 
203       throws NoSuchMethodException {
204     Enumeration<Column<?>> columns = persistent.getTable().columns();
205     while (columns.hasMoreElements()) {
206       Column<?> col = (Column<?>)columns.nextElement();
207       if(col.isTroidColumn() && !(pojo instanceof Persistent)) continue;
208       Object cooked = col.getCooked(persistent);
209       if (cooked != null) {
210         String setterName = "set" + StringUtils.capitalised(col.getName());
211         Method[] possibleSetters = ClassUtils.getOneArgumentMethods(pojo.getClass(), setterName);
212         if(possibleSetters.length == 0)
213             throw new NoSuchMethodException("No setter called " + setterName
214                     + " could be found " + "on Class " + pojo.getClass().getName());
215         for (int i = 0; i < possibleSetters.length; i++) {
216           if (col.getType() instanceof ReferencePoemType) {
217             Object newPojo = PersistentFactory.from((Persistent)cooked,
218                     possibleSetters[i].getParameterTypes()[0]);
219             try {
220               possibleSetters[i].invoke(pojo, new Object[] { newPojo });
221             } catch (Exception e) {
222               throw new AppBugPoemException(
223                       "Problem setting value of Column " + col.getName(), e);
224             }
225           } else {
226             try {
227               possibleSetters[i].invoke(pojo, new Object[] { cooked });
228             } catch (Exception e) {
229               throw new AppBugPoemException(
230                       "Problem setting value of Column " + col.getName(), e);
231             }
232           }
233         }
234       }
235     }
236     return pojo;
237   }
238 
239 
240 }