001    /**
002     * Copyright 2010 The Kuali Foundation Licensed under the
003     * Educational Community License, Version 2.0 (the "License"); you may
004     * not use this file except in compliance with the License. You may
005     * obtain a copy of the License at
006     *
007     * http://www.osedu.org/licenses/ECL-2.0
008     *
009     * Unless required by applicable law or agreed to in writing,
010     * software distributed under the License is distributed on an "AS IS"
011     * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012     * or implied. See the License for the specific language governing
013     * permissions and limitations under the License.
014     */
015    
016    package org.kuali.student.common.ui.client.application;
017    
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.HashSet;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Map.Entry;
025    
026    import org.kuali.student.common.messages.dto.Message;
027    import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
028    import org.kuali.student.common.ui.client.mvc.HasCrossConstraints;
029    import org.kuali.student.common.ui.client.security.SecurityContext;
030    import org.kuali.student.common.ui.client.service.ServerPropertiesRpcService;
031    import org.kuali.student.common.ui.client.service.ServerPropertiesRpcServiceAsync;
032    
033    import com.google.gwt.core.client.GWT;
034    import com.google.gwt.user.client.rpc.AsyncCallback;
035    
036    /**
037     * The application contains information about who is currently logged in, the security context, and access
038     * to messages loaded from the message service in the app.  It provides and a static way to obtain this
039     * information across the entire app.
040     * 
041     * @author Kuali Student
042     *
043     */
044    public class ApplicationContext {
045            private ServerPropertiesRpcServiceAsync serverPropertiesRpcService = GWT.create(ServerPropertiesRpcService.class);
046            
047            private boolean loggedIn = true;
048            private String userId = "testuser";
049            private String version = "KS";
050            private List<String> roles = new ArrayList<String>();
051            
052            private Map<String, Map<String, String>> messages = new HashMap<String, Map<String,String>>();
053            private Map<String, String> flatMessages = new HashMap<String, String>();
054            private List<Message> messagesList = new ArrayList<Message>();
055            
056            private SecurityContext securityContext;
057            private String applicationContextUrl;
058            
059            //These maps are used to store query paths to their corresponding fieldDefinitions, and also whcih fields have cross constraints
060            private String parentPath = "";
061            private HashMap<String,HashMap<String,FieldDescriptor>> pathToFieldMapping = new HashMap<String,HashMap<String,FieldDescriptor>>();
062            private HashMap<String,HashMap<String,HashSet<HasCrossConstraints>>> crossConstraints = new HashMap<String,HashMap<String,HashSet<HasCrossConstraints>>>();
063    //      private HashMap<String,HashMap<FieldDescriptor, String>> defaultValueMapping = new HashMap<String,HashMap<FieldDescriptor, String>>();
064            /**
065             * This constructor should only be visible to the common application package. If ApplicationContext is 
066             * required outside this package do Application.getApplicationContext();
067             */
068            protected ApplicationContext() {
069                    roles.add("role1");
070                    roles.add("role2");
071                    
072                    serverPropertiesRpcService.getContextPath(new AsyncCallback<String>(){
073    
074                            @Override
075                            public void onFailure(Throwable caught) {
076                                    throw new RuntimeException("Fatal - Unable to initialze application context");
077                            }
078    
079                            @Override
080                            public void onSuccess(String result) {
081                                    applicationContextUrl = result;
082                            }                       
083                    });
084            }
085    
086            public void setLoggedIn(boolean loggedIn) {
087                    this.loggedIn = loggedIn;
088            }
089    
090            public void setUserId(String userId) {
091                    this.userId = userId;
092            }
093    
094            public void setRoles(List<String> roles) {
095                    this.roles = roles;
096            }
097    
098            public boolean isLoggedIn() {
099                    return loggedIn;
100            }
101    
102            public String getUserId() {
103                    return userId;
104            }
105    
106            public List<String> getRoles() {
107                    return roles;
108            }
109    
110        /**
111         * Adds the messages in the list of messages to the map of the messages
112         * @param messages
113         */
114        public void addMessages(List<Message> messages) {
115                    messagesList.addAll(messages);
116                for (Message m : messages) {
117                    String groupName = m.getGroupName();
118                    Map<String, String> group = this.messages.get(groupName);
119                    if (group == null) {
120                        group = new HashMap<String, String>();
121                        this.messages.put(groupName, group);
122                    }
123                    group.put(m.getId(), m.getValue());
124                    flatMessages.put(m.getId(), m.getValue());
125                }
126            }
127            
128            /**
129             * Get a message by a unique id
130             */
131            public String getMessage(String messageId) {
132                return flatMessages.get(messageId);
133        }
134        
135            /**
136             * Returns all the messages in the ApplicationContext
137             */
138            public List<Message> getMessages() {
139                return messagesList;
140        }
141        
142            
143            /**
144             * Get message by the group it is in and its unique id within that group
145             */
146            public String getMessage(String groupName, String messageId) {
147                            
148                String result = null;
149                
150                Map<String, String> group = this.messages.get(groupName);
151                if (group != null) {
152                    result = group.get(messageId);
153                }
154                
155                return result;
156            }
157            
158        /**
159         * 
160         * This method looks up a UI Label in the messages cache.  
161         * First looks for a label specific to the type and state of the field.
162         * If none found try for a generalized label.
163         * Otherwise return the supplied fieldId
164         * Groups provide namespace for same label ids within different LUs
165         * 
166         * @param groupName - for example 'course' or 'program'
167         * @param type
168         * @param state
169         * @param fieldId
170         * @return
171         */
172             public String getUILabel(String groupName, String type, String state, String fieldId) {
173    
174            String label = getMessage(groupName, type + ":" + state + ":" + fieldId);
175            
176            if (label == null)
177                label = getMessage(groupName, fieldId);
178            
179            if (label == null)
180                label =  fieldId;
181            
182            return label;
183            
184        }
185             
186            /**
187             * Same as getUILabel(String groupName, String type, String state, String fieldId) with no
188             * type and state needed
189             */
190            public String getUILabel(String groupName, String fieldId) {
191    
192                    String label = getMessage(groupName, fieldId);
193                    
194                    if (label == null)
195                        label =  fieldId;
196                    
197                    return label;
198                    
199            }
200    
201        /**
202         * Get the security context for the app
203         * @return SecurityContext
204         */
205        public SecurityContext getSecurityContext() {
206            return securityContext;
207        }
208    
209        public void setSecurityContext(SecurityContext securityContext) {
210            this.securityContext = securityContext;
211        }
212            
213            /**
214             * Application URL based on the serverPropertiesRPC service result
215             */
216            public String getApplicationContextUrl() {
217                    return applicationContextUrl;
218            }
219    
220            public void setVersion(String version) {
221                    this.version = version;
222            }
223    
224            public String getVersion() {
225                    return version;
226            }
227            
228            /**
229             * Adds a mapping from path to a list of field descriptors for a given namespace
230             * namespace defaults to _default if null
231             * @param path
232             * @param fd
233             */
234            public void putCrossConstraint(String namespace, String path, HasCrossConstraints fd){
235                    if(namespace==null){
236                            namespace="_default";
237                    }
238                    
239                    HashMap<String,HashSet<HasCrossConstraints>> crossConstraintMap = crossConstraints.get(namespace);
240                    if(crossConstraintMap==null){
241                            crossConstraintMap = new HashMap<String,HashSet<HasCrossConstraints>>();
242                            crossConstraints.put(namespace, crossConstraintMap);
243                    }
244                    HashSet<HasCrossConstraints> fieldDescriptors = crossConstraintMap.get(path);
245                    if(fieldDescriptors == null){
246                            fieldDescriptors = new HashSet<HasCrossConstraints>();
247                            crossConstraintMap.put(path, fieldDescriptors);
248                    }
249                    fieldDescriptors.add(fd);
250            }
251    
252            
253            
254            public HashSet<HasCrossConstraints> getCrossConstraint(String namespace, String path){
255                    if(namespace==null){
256                            namespace="_default";
257                    }
258                    HashMap<String,HashSet<HasCrossConstraints>> crossConstraintMap = crossConstraints.get(namespace);
259                    if(crossConstraintMap!=null){
260                            return crossConstraintMap.get(path);
261                    }
262                    return null;
263            }
264            public void clearCrossConstraintMap(String namespace){
265                    if(namespace==null){
266                            namespace="_default";
267                    }
268                    crossConstraints.remove(namespace);
269            }
270            public void putPathToFieldMapping(String namespace, String path, FieldDescriptor fd){
271                    if(namespace==null){
272                            namespace="_default";
273                    }
274                    
275                    HashMap<String,FieldDescriptor> pathToField = pathToFieldMapping.get(namespace);
276                    if(pathToField==null){
277                            pathToField = new HashMap<String,FieldDescriptor>();
278                            pathToFieldMapping.put(namespace, pathToField);
279                    }
280                    pathToField.put(path, fd);
281            }
282    
283            public FieldDescriptor getPathToFieldMapping(String namespace, String path){
284                    if(namespace==null){
285                            namespace="_default";
286                    }
287                    
288                    HashMap<String,FieldDescriptor> pathToField = pathToFieldMapping.get(namespace);
289                    if(pathToField!=null){
290                            return pathToField.get(path);
291                    }
292                    return null;
293            }
294            public void clearPathToFieldMapping(String namespace){
295                    if(namespace==null){
296                            namespace="_default";
297                    }
298                    pathToFieldMapping.remove(namespace);
299            }
300    
301            /**
302             * Removes the bidirectional mapping for all paths that start with the path prefix
303             * This means if Field A had a dependency on Field B, and you cleared A, first all mappings with
304             * dependencies to A would be removed, then all mappings with dependencies to A would be removed. 
305             * @param namespace
306             * @param pathPrefix
307             */
308            public void clearCrossConstraintsWithStartingPath(String namespace, String pathPrefix){
309                    if(namespace==null){
310                            namespace="_default";
311                    }
312                    //First delete any cross constraint mappings based on this field
313                    HashMap<String,HashSet<HasCrossConstraints>> crossConstraintMap = crossConstraints.get(namespace);
314                    if(crossConstraintMap!=null){
315                            Iterator<Map.Entry<String,HashSet<HasCrossConstraints>>> constraintMapIter = crossConstraintMap.entrySet().iterator();
316                            while(constraintMapIter.hasNext()){
317                                    Map.Entry<String,HashSet<HasCrossConstraints>> entry = constraintMapIter.next();
318                                    if(entry.getKey().startsWith(pathPrefix)){
319                                            constraintMapIter.remove();
320                                    }
321                            }
322    
323                            //Find all the fieldDescriptors that start with the prefix and remove the cross constraint mapping to them 
324                            HashMap<String,FieldDescriptor> pathToField = pathToFieldMapping.get(namespace);
325                            if(pathToField!=null){
326                                    Iterator<Entry<String, FieldDescriptor>> pathMapIter = pathToField.entrySet().iterator();
327                                    while(pathMapIter.hasNext()){
328                                            Entry<String, FieldDescriptor> entry = pathMapIter.next();
329                                            if(entry.getKey().startsWith(pathPrefix)){
330                                                    FieldDescriptor fd = entry.getValue();
331                                                    if(fd.getFieldWidget()!=null && fd.getFieldWidget() instanceof HasCrossConstraints && ((HasCrossConstraints)fd.getFieldWidget()).getCrossConstraints()!=null){
332                                                            //Loop through the constraint paths and remove any mapping to the existing field descriptor
333                                                            for(String path:((HasCrossConstraints)fd.getFieldWidget()).getCrossConstraints()){
334                                                                    HashSet<HasCrossConstraints> set = crossConstraintMap.get(path);
335                                                                    if(set!=null){
336                                                                            set.remove(fd.getFieldWidget());
337                                                                    }
338                                                            }
339                                                    }
340                                            }
341                                    }
342                            }
343                    }
344            }
345    
346            
347            public HashSet<HasCrossConstraints> getCrossConstraints(String namespace) {
348                    if(namespace==null){
349                            namespace="_default";
350                    }
351                    HashSet<HasCrossConstraints> results = new HashSet<HasCrossConstraints>();
352                    HashMap<String,HashSet<HasCrossConstraints>> crossConstraintMap = crossConstraints.get(namespace);
353                    if(crossConstraintMap!=null){
354                            for(HashSet<HasCrossConstraints> fds: crossConstraintMap.values()){
355                                    results.addAll(fds);
356                            }
357                    }
358                    return results;
359            }
360    
361            public String getParentPath() {
362                    return parentPath;
363            }
364    
365            public void setParentPath(String parentPath) {
366                    this.parentPath = parentPath;
367            }
368    
369    //      public void putDefaultValueMapping(String namespace,
370    //                      FieldDescriptor fieldDescriptor, String defaultValuePath) {
371    //              if(namespace==null){
372    //                      namespace="_default";
373    //              }
374    //              HashMap<FieldDescriptor, String> defaultValueMap = defaultValueMapping.get(namespace);
375    //              if(defaultValueMap==null){
376    //                      defaultValueMap = new HashMap<FieldDescriptor, String>();
377    //                      defaultValueMapping.put(namespace, defaultValueMap);
378    //              }
379    //              defaultValueMap.put(fieldDescriptor, defaultValuePath);
380    //      }
381    //
382    //      public HashMap<FieldDescriptor, String> getDefaultValueMapping(String namespace) {
383    //              if(namespace==null){
384    //                      namespace="_default";
385    //              }
386    //              HashMap<FieldDescriptor, String> result = defaultValueMapping.get(namespace);
387    //              if(result==null){
388    //                      result = new HashMap<FieldDescriptor, String>();
389    //              }
390    //              return result;
391    //      }
392    //      public void clearDefaultValueMapping(String namespace){
393    //              if(namespace==null){
394    //                      namespace="_default";
395    //              }
396    //              defaultValueMapping.remove(namespace);
397    //      }
398    
399    }