1 /*
2  * Copyright 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.google.gwt.sample.showcase.client.content.cell;
17 
18 import com.google.gwt.core.client.GWT;
19 import com.google.gwt.i18n.client.Constants;
20 import com.google.gwt.user.client.Random;
21 import com.google.gwt.view.client.HasData;
22 import com.google.gwt.view.client.ListDataProvider;
23 import com.google.gwt.view.client.ProvidesKey;
24 
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 
33 /**
34  * The data source for contact information used in the sample.
35  */
36 public class ContactDatabase {
37 
38   /**
39    * A contact category.
40    */
41   public static class Category {
42 
43     private final String displayName;
44 
Category(String displayName)45     private Category(String displayName) {
46       this.displayName = displayName;
47     }
48 
getDisplayName()49     public String getDisplayName() {
50       return displayName;
51     }
52   }
53 
54   /**
55    * Information about a contact.
56    */
57   public static class ContactInfo implements Comparable<ContactInfo> {
58 
59     /**
60      * The key provider that provides the unique ID of a contact.
61      */
62     public static final ProvidesKey<ContactInfo> KEY_PROVIDER = new ProvidesKey<ContactInfo>() {
63       @Override
64       public Object getKey(ContactInfo item) {
65         return item == null ? null : item.getId();
66       }
67     };
68 
69     private static int nextId = 0;
70 
71     private String address;
72     private int age;
73     private Date birthday;
74     private Category category;
75     private String firstName;
76     private final int id;
77     private String lastName;
78 
ContactInfo(Category category)79     public ContactInfo(Category category) {
80       this.id = nextId;
81       nextId++;
82       setCategory(category);
83     }
84 
85     @Override
compareTo(ContactInfo o)86     public int compareTo(ContactInfo o) {
87       return (o == null || o.firstName == null) ? -1 : -o.firstName.compareTo(firstName);
88     }
89 
90     @Override
equals(Object o)91     public boolean equals(Object o) {
92       if (o instanceof ContactInfo) {
93         return id == ((ContactInfo) o).id;
94       }
95       return false;
96     }
97 
98     /**
99      * @return the contact's address
100      */
getAddress()101     public String getAddress() {
102       return address;
103     }
104 
105     /**
106      * @return the contact's age
107      */
getAge()108     public int getAge() {
109       return age;
110     }
111 
112     /**
113      * @return the contact's birthday
114      */
getBirthday()115     public Date getBirthday() {
116       return birthday;
117     }
118 
119     /**
120      * @return the category of the conteact
121      */
getCategory()122     public Category getCategory() {
123       return category;
124     }
125 
126     /**
127      * @return the contact's firstName
128      */
getFirstName()129     public String getFirstName() {
130       return firstName;
131     }
132 
133     /**
134      * @return the contact's full name
135      */
getFullName()136     public final String getFullName() {
137       return firstName + " " + lastName;
138     }
139 
140     /**
141      * @return the unique ID of the contact
142      */
getId()143     public int getId() {
144       return this.id;
145     }
146 
147     /**
148      * @return the contact's lastName
149      */
getLastName()150     public String getLastName() {
151       return lastName;
152     }
153 
154     @Override
hashCode()155     public int hashCode() {
156       return id;
157     }
158 
159     /**
160      * Set the contact's address.
161      *
162      * @param address the address
163      */
setAddress(String address)164     public void setAddress(String address) {
165       this.address = address;
166     }
167 
168     /**
169      * Set the contact's birthday.
170      *
171      * @param birthday the birthday
172      */
173     @SuppressWarnings("deprecation")
setBirthday(Date birthday)174     public void setBirthday(Date birthday) {
175       this.birthday = birthday;
176 
177       // Recalculate the age.
178       Date today = new Date();
179       this.age = today.getYear() - birthday.getYear();
180       if (today.getMonth() > birthday.getMonth()
181           || (today.getMonth() == birthday.getMonth() && today.getDate() > birthday.getDate())) {
182         this.age--;
183       }
184     }
185 
186     /**
187      * Set the contact's category.
188      *
189      * @param category the category to set
190      */
setCategory(Category category)191     public void setCategory(Category category) {
192       assert category != null : "category cannot be null";
193       this.category = category;
194     }
195 
196     /**
197      * Set the contact's first name.
198      *
199      * @param firstName the firstName to set
200      */
setFirstName(String firstName)201     public void setFirstName(String firstName) {
202       this.firstName = firstName;
203     }
204 
205     /**
206      * Set the contact's last name.
207      *
208      * @param lastName the lastName to set
209      */
setLastName(String lastName)210     public void setLastName(String lastName) {
211       this.lastName = lastName;
212     }
213   }
214 
215   /**
216    * The constants used in this Content Widget.
217    */
218   static interface DatabaseConstants extends Constants {
contactDatabaseCategories()219     String[] contactDatabaseCategories();
220   }
221 
222   private static final String[] FEMALE_FIRST_NAMES = {
223       "Mary", "Patricia", "Linda", "Barbara", "Elizabeth", "Jennifer", "Maria", "Susan",
224       "Margaret", "Dorothy", "Lisa", "Nancy", "Karen", "Betty", "Helen", "Sandra", "Donna",
225       "Carol", "Ruth", "Sharon", "Michelle", "Laura", "Sarah", "Kimberly", "Deborah", "Jessica",
226       "Shirley", "Cynthia", "Angela", "Melissa", "Brenda", "Amy", "Anna", "Rebecca", "Virginia",
227       "Kathleen", "Pamela", "Martha", "Debra", "Amanda", "Stephanie", "Carolyn", "Christine",
228       "Marie", "Janet", "Catherine", "Frances", "Ann", "Joyce", "Diane", "Alice", "Julie",
229       "Heather", "Teresa", "Doris", "Gloria", "Evelyn", "Jean", "Cheryl", "Mildred", "Katherine",
230       "Joan", "Ashley", "Judith", "Rose", "Janice", "Kelly", "Nicole", "Judy", "Christina",
231       "Kathy", "Theresa", "Beverly", "Denise", "Tammy", "Irene", "Jane", "Lori", "Rachel",
232       "Marilyn", "Andrea", "Kathryn", "Louise", "Sara", "Anne", "Jacqueline", "Wanda", "Bonnie",
233       "Julia", "Ruby", "Lois", "Tina", "Phyllis", "Norma", "Paula", "Diana", "Annie", "Lillian",
234       "Emily", "Robin", "Peggy", "Crystal", "Gladys", "Rita", "Dawn", "Connie", "Florence",
235       "Tracy", "Edna", "Tiffany", "Carmen", "Rosa", "Cindy", "Grace", "Wendy", "Victoria", "Edith",
236       "Kim", "Sherry", "Sylvia", "Josephine", "Thelma", "Shannon", "Sheila", "Ethel", "Ellen",
237       "Elaine", "Marjorie", "Carrie", "Charlotte", "Monica", "Esther", "Pauline", "Emma",
238       "Juanita", "Anita", "Rhonda", "Hazel", "Amber", "Eva", "Debbie", "April", "Leslie", "Clara",
239       "Lucille", "Jamie", "Joanne", "Eleanor", "Valerie", "Danielle", "Megan", "Alicia", "Suzanne",
240       "Michele", "Gail", "Bertha", "Darlene", "Veronica", "Jill", "Erin", "Geraldine", "Lauren",
241       "Cathy", "Joann", "Lorraine", "Lynn", "Sally", "Regina", "Erica", "Beatrice", "Dolores",
242       "Bernice", "Audrey", "Yvonne", "Annette", "June", "Samantha", "Marion", "Dana", "Stacy",
243       "Ana", "Renee", "Ida", "Vivian", "Roberta", "Holly", "Brittany", "Melanie", "Loretta",
244       "Yolanda", "Jeanette", "Laurie", "Katie", "Kristen", "Vanessa", "Alma", "Sue", "Elsie",
245       "Beth", "Jeanne"};
246   private static final String[] MALE_FIRST_NAMES = {
247       "James", "John", "Robert", "Michael", "William", "David", "Richard", "Charles", "Joseph",
248       "Thomas", "Christopher", "Daniel", "Paul", "Mark", "Donald", "George", "Kenneth", "Steven",
249       "Edward", "Brian", "Ronald", "Anthony", "Kevin", "Jason", "Matthew", "Gary", "Timothy",
250       "Jose", "Larry", "Jeffrey", "Frank", "Scott", "Eric", "Stephen", "Andrew", "Raymond",
251       "Gregory", "Joshua", "Jerry", "Dennis", "Walter", "Patrick", "Peter", "Harold", "Douglas",
252       "Henry", "Carl", "Arthur", "Ryan", "Roger", "Joe", "Juan", "Jack", "Albert", "Jonathan",
253       "Justin", "Terry", "Gerald", "Keith", "Samuel", "Willie", "Ralph", "Lawrence", "Nicholas",
254       "Roy", "Benjamin", "Bruce", "Brandon", "Adam", "Harry", "Fred", "Wayne", "Billy", "Steve",
255       "Louis", "Jeremy", "Aaron", "Randy", "Howard", "Eugene", "Carlos", "Russell", "Bobby",
256       "Victor", "Martin", "Ernest", "Phillip", "Todd", "Jesse", "Craig", "Alan", "Shawn",
257       "Clarence", "Sean", "Philip", "Chris", "Johnny", "Earl", "Jimmy", "Antonio", "Danny",
258       "Bryan", "Tony", "Luis", "Mike", "Stanley", "Leonard", "Nathan", "Dale", "Manuel", "Rodney",
259       "Curtis", "Norman", "Allen", "Marvin", "Vincent", "Glenn", "Jeffery", "Travis", "Jeff",
260       "Chad", "Jacob", "Lee", "Melvin", "Alfred", "Kyle", "Francis", "Bradley", "Jesus", "Herbert",
261       "Frederick", "Ray", "Joel", "Edwin", "Don", "Eddie", "Ricky", "Troy", "Randall", "Barry",
262       "Alexander", "Bernard", "Mario", "Leroy", "Francisco", "Marcus", "Micheal", "Theodore",
263       "Clifford", "Miguel", "Oscar", "Jay", "Jim", "Tom", "Calvin", "Alex", "Jon", "Ronnie",
264       "Bill", "Lloyd", "Tommy", "Leon", "Derek", "Warren", "Darrell", "Jerome", "Floyd", "Leo",
265       "Alvin", "Tim", "Wesley", "Gordon", "Dean", "Greg", "Jorge", "Dustin", "Pedro", "Derrick",
266       "Dan", "Lewis", "Zachary", "Corey", "Herman", "Maurice", "Vernon", "Roberto", "Clyde",
267       "Glen", "Hector", "Shane", "Ricardo", "Sam", "Rick", "Lester", "Brent", "Ramon", "Charlie",
268       "Tyler", "Gilbert", "Gene"};
269   private static final String[] LAST_NAMES = {
270       "Smith", "Johnson", "Williams", "Jones", "Brown", "Davis", "Miller", "Wilson", "Moore",
271       "Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin", "Thompson", "Garcia",
272       "Martinez", "Robinson", "Clark", "Rodriguez", "Lewis", "Lee", "Walker", "Hall", "Allen",
273       "Young", "Hernandez", "King", "Wright", "Lopez", "Hill", "Scott", "Green", "Adams", "Baker",
274       "Gonzalez", "Nelson", "Carter", "Mitchell", "Perez", "Roberts", "Turner", "Phillips",
275       "Campbell", "Parker", "Evans", "Edwards", "Collins", "Stewart", "Sanchez", "Morris",
276       "Rogers", "Reed", "Cook", "Morgan", "Bell", "Murphy", "Bailey", "Rivera", "Cooper",
277       "Richardson", "Cox", "Howard", "Ward", "Torres", "Peterson", "Gray", "Ramirez", "James",
278       "Watson", "Brooks", "Kelly", "Sanders", "Price", "Bennett", "Wood", "Barnes", "Ross",
279       "Henderson", "Coleman", "Jenkins", "Perry", "Powell", "Long", "Patterson", "Hughes",
280       "Flores", "Washington", "Butler", "Simmons", "Foster", "Gonzales", "Bryant", "Alexander",
281       "Russell", "Griffin", "Diaz", "Hayes", "Myers", "Ford", "Hamilton", "Graham", "Sullivan",
282       "Wallace", "Woods", "Cole", "West", "Jordan", "Owens", "Reynolds", "Fisher", "Ellis",
283       "Harrison", "Gibson", "Mcdonald", "Cruz", "Marshall", "Ortiz", "Gomez", "Murray", "Freeman",
284       "Wells", "Webb", "Simpson", "Stevens", "Tucker", "Porter", "Hunter", "Hicks", "Crawford",
285       "Henry", "Boyd", "Mason", "Morales", "Kennedy", "Warren", "Dixon", "Ramos", "Reyes", "Burns",
286       "Gordon", "Shaw", "Holmes", "Rice", "Robertson", "Hunt", "Black", "Daniels", "Palmer",
287       "Mills", "Nichols", "Grant", "Knight", "Ferguson", "Rose", "Stone", "Hawkins", "Dunn",
288       "Perkins", "Hudson", "Spencer", "Gardner", "Stephens", "Payne", "Pierce", "Berry",
289       "Matthews", "Arnold", "Wagner", "Willis", "Ray", "Watkins", "Olson", "Carroll", "Duncan",
290       "Snyder", "Hart", "Cunningham", "Bradley", "Lane", "Andrews", "Ruiz", "Harper", "Fox",
291       "Riley", "Armstrong", "Carpenter", "Weaver", "Greene", "Lawrence", "Elliott", "Chavez",
292       "Sims", "Austin", "Peters", "Kelley", "Franklin", "Lawson"};
293   private static final String[] STREET_NAMES =
294       {
295           "Peachtree", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Tenth",
296           "Fourteenth", "Spring", "Techwood", "West Peachtree", "Juniper", "Cypress", "Fowler",
297           "Piedmont", "Juniper", "Main", "Central", "Currier", "Courtland", "Williams",
298           "Centennial", "Olympic", "Baker", "Highland", "Pryor", "Decatur", "Bell", "Edgewood",
299           "Mitchell", "Forsyth", "Capital"};
300   private static final String[] STREET_SUFFIX = {
301       "St", "Rd", "Ln", "Blvd", "Way", "Pkwy", "Cir", "Ave"};
302 
303   /**
304    * The singleton instance of the database.
305    */
306   private static ContactDatabase instance;
307 
308   /**
309    * Get the singleton instance of the contact database.
310    *
311    * @return the singleton instance
312    */
get()313   public static ContactDatabase get() {
314     if (instance == null) {
315       instance = new ContactDatabase();
316     }
317     return instance;
318   }
319 
320   /**
321    * The provider that holds the list of contacts in the database.
322    */
323   private ListDataProvider<ContactInfo> dataProvider = new ListDataProvider<ContactInfo>();
324 
325   private final Category[] categories;
326 
327   /**
328    * The map of contacts to her friends.
329    */
330   private final Map<Integer, Set<ContactInfo>> friendsMap =
331       new HashMap<Integer, Set<ContactInfo>>();
332 
333   /**
334    * Construct a new contact database.
335    */
ContactDatabase()336   private ContactDatabase() {
337     // Initialize the categories.
338     DatabaseConstants constants = GWT.create(DatabaseConstants.class);
339     String[] catNames = constants.contactDatabaseCategories();
340     categories = new Category[catNames.length];
341     for (int i = 0; i < catNames.length; i++) {
342       categories[i] = new Category(catNames[i]);
343     }
344 
345     // Generate initial data.
346     generateContacts(250);
347   }
348 
349   /**
350    * Add a new contact.
351    *
352    * @param contact the contact to add.
353    */
addContact(ContactInfo contact)354   public void addContact(ContactInfo contact) {
355     List<ContactInfo> contacts = dataProvider.getList();
356     // Remove the contact first so we don't add a duplicate.
357     contacts.remove(contact);
358     contacts.add(contact);
359   }
360 
361   /**
362    * Add a display to the database. The current range of interest of the display
363    * will be populated with data.
364    *
365    * @param display a {@Link HasData}.
366    */
addDataDisplay(HasData<ContactInfo> display)367   public void addDataDisplay(HasData<ContactInfo> display) {
368     dataProvider.addDataDisplay(display);
369   }
370 
371   /**
372    * Generate the specified number of contacts and add them to the data
373    * provider.
374    *
375    * @param count the number of contacts to generate.
376    */
generateContacts(int count)377   public void generateContacts(int count) {
378     List<ContactInfo> contacts = dataProvider.getList();
379     for (int i = 0; i < count; i++) {
380       contacts.add(createContactInfo());
381     }
382   }
383 
getDataProvider()384   public ListDataProvider<ContactInfo> getDataProvider() {
385     return dataProvider;
386   }
387 
388   /**
389    * Get the categories in the database.
390    *
391    * @return the categories in the database
392    */
queryCategories()393   public Category[] queryCategories() {
394     return categories;
395   }
396 
397   /**
398    * Query all contacts for the specified category.
399    *
400    * @param category the category
401    * @return the list of contacts in the category
402    */
queryContactsByCategory(Category category)403   public List<ContactInfo> queryContactsByCategory(Category category) {
404     List<ContactInfo> matches = new ArrayList<ContactInfo>();
405     for (ContactInfo contact : dataProvider.getList()) {
406       if (contact.getCategory() == category) {
407         matches.add(contact);
408       }
409     }
410     return matches;
411   }
412 
413   /**
414    * Query all contacts for the specified category that begin with the specified
415    * first name prefix.
416    *
417    * @param category the category
418    * @param firstNamePrefix the prefix of the first name
419    * @return the list of contacts in the category
420    */
queryContactsByCategoryAndFirstName(Category category, String firstNamePrefix)421   public List<ContactInfo> queryContactsByCategoryAndFirstName(Category category,
422       String firstNamePrefix) {
423     List<ContactInfo> matches = new ArrayList<ContactInfo>();
424     for (ContactInfo contact : dataProvider.getList()) {
425       if (contact.getCategory() == category && contact.getFirstName().startsWith(firstNamePrefix)) {
426         matches.add(contact);
427       }
428     }
429     return matches;
430   }
431 
432   /**
433    * Query the list of friends for the specified contact.
434    *
435    * @param contact the contact
436    * @return the friends of the contact
437    */
queryFriends(ContactInfo contact)438   public Set<ContactInfo> queryFriends(ContactInfo contact) {
439     Set<ContactInfo> friends = friendsMap.get(contact.getId());
440     if (friends == null) {
441       // Assign some random friends.
442       friends = new HashSet<ContactInfo>();
443       int numContacts = dataProvider.getList().size();
444       int friendCount = 2 + Random.nextInt(8);
445       for (int i = 0; i < friendCount; i++) {
446         friends.add(dataProvider.getList().get(Random.nextInt(numContacts)));
447       }
448       friendsMap.put(contact.getId(), friends);
449     }
450     return friends;
451   }
452 
453   /**
454    * Refresh all displays.
455    */
refreshDisplays()456   public void refreshDisplays() {
457     dataProvider.refresh();
458   }
459 
460   /**
461    * Create a new random {@link ContactInfo}.
462    *
463    * @return the new {@link ContactInfo}.
464    */
465   @SuppressWarnings("deprecation")
createContactInfo()466   private ContactInfo createContactInfo() {
467     ContactInfo contact = new ContactInfo(nextValue(categories));
468     contact.setLastName(nextValue(LAST_NAMES));
469     if (Random.nextBoolean()) {
470       // Male.
471       contact.setFirstName(nextValue(MALE_FIRST_NAMES));
472     } else {
473       // Female.
474       contact.setFirstName(nextValue(FEMALE_FIRST_NAMES));
475     }
476 
477     // Create a birthday between 20-80 years ago.
478     int year = (new Date()).getYear() - 21 - Random.nextInt(61);
479     contact.setBirthday(new Date(year, Random.nextInt(12), 1 + Random.nextInt(31)));
480 
481     // Create an address.
482     int addrNum = 1 + Random.nextInt(999);
483     String addrStreet = nextValue(STREET_NAMES);
484     String addrSuffix = nextValue(STREET_SUFFIX);
485     contact.setAddress(addrNum + " " + addrStreet + " " + addrSuffix);
486     return contact;
487   }
488 
489   /**
490    * Get the next random value from an array.
491    *
492    * @param array the array
493    * @return a random value in the array
494    */
nextValue(T[] array)495   private <T> T nextValue(T[] array) {
496     return array[Random.nextInt(array.length)];
497   }
498 
499 }
500