View Javadoc

1   /*
2    * Created on Apr 25, 2005, Copyright UC Regents
3    */
4   package org.telscenter.pas.beans;
5   
6   import java.awt.event.WindowAdapter;
7   import java.awt.event.WindowEvent;
8   import java.awt.event.WindowListener;
9   import java.beans.PropertyChangeListener;
10  import java.beans.PropertyVetoException;
11  import java.beans.beancontext.BeanContext;
12  import java.beans.beancontext.BeanContextChildSupport;
13  import java.beans.beancontext.BeanContextServices;
14  import java.io.File;
15  import java.io.FileOutputStream;
16  import java.io.IOException;
17  import java.lang.reflect.Method;
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.TooManyListenersException;
23  import java.util.logging.Logger;
24  
25  import javax.swing.ProgressMonitor;
26  import javax.swing.WindowConstants;
27  
28  import net.sf.sail.common.beansupport.ITitleAware;
29  import net.sf.sail.common.beansupport.SailBeanContextChildSupport;
30  import net.sf.sail.core.beans.Pod;
31  import net.sf.sail.core.beans.event.SessionEvent;
32  import net.sf.sail.core.beans.event.SessionEventListener;
33  import net.sf.sail.core.beans.service.AgentService;
34  import net.sf.sail.core.beans.service.AnnotationService;
35  import net.sf.sail.core.beans.service.SessionService;
36  import net.sf.sail.core.entity.IAgent;
37  import net.sf.sail.core.entity.ISock;
38  import net.sf.sail.core.entity.MismatchedAgentSetSizeException;
39  import net.sf.sail.core.entity.Rim;
40  import net.sf.sail.core.entity.Role;
41  import net.sf.sail.core.entity.UnsupportedRimShapeException;
42  
43  import org.jdom.DefaultJDOMFactory;
44  import org.jdom.Document;
45  import org.jdom.Element;
46  import org.jdom.output.XMLOutputter;
47  import org.telscenter.pas.navigation.PasProjectNavigationTaskPanel;
48  import org.telscenter.pas.navigation.events.NavigationListener;
49  import org.telscenter.pas.service.PasProjectServiceProvider;
50  import org.telscenter.pas.steps.Assessment;
51  import org.telscenter.pas.ui.beans.issueReporter.IssueReporter;
52  import org.telscenter.pas.ui.frames.PasFrame;
53  import org.telscenter.pas.ui.pdf.actions.GeneratePDFAction;
54  
55  /***
56   * Bean representing the Pas project.
57   * 
58   * @author turadg
59   */
60  public class PasProject extends SailBeanContextChildSupport implements
61  		IAuthorNotation, ITitleAware {
62  	/***
63  	 * 
64  	 */
65  	public static final String ACTIVITIES = "activities";
66  
67  	/***
68  	 * Logger for this class
69  	 */
70  	private static final Logger logger = Logger.getLogger(PasProject.class
71  			.getName());
72  
73  	private static final long serialVersionUID = 1L;
74  
75  	List<PasActivity> activitiesList = new ArrayList<PasActivity>();
76  
77  	String authorNotes = new String();
78  
79  	String title;
80  
81  	private transient PasProjectServiceProvider pasServiceProvider;
82  
83  	private transient PasProjectNavigationTaskPanel pasProjectNavigationPanel;
84  
85  	private PasFrame projectFrame;
86  
87  	private String sessionTopic;
88  
89  	private AgentService agentService;
90  	private SessionService sessionService;
91  	private AnnotationService annotationService;
92  
93  	private Rim<String> navigationLogRim;
94  	private Rim<String> sessionStateRim;
95  	private Rim<String> curnitMapRim;
96  	private ISock<String> navigationLogSock;
97  	private ISock<String> sessionStateSock;
98  	private ISock<String> curnitMapSock;
99  	private Map<String, BeanContextChildSupport> itemParentMap = new HashMap<String, BeanContextChildSupport>();
100 	private DefaultJDOMFactory domFactory = new DefaultJDOMFactory();
101 	private XMLOutputter outputter = new XMLOutputter();
102 	private Document document; // document that contains curnitmap
103 
104 	private boolean showFirstStepOnLoad = true;
105 
106 	private transient boolean exitOnClose = true;
107 
108 	private boolean autoNumbering = true;
109 	
110 	private boolean authoringMode = false;
111 
112 	private HashMap projectProperties;
113 	// Check that we are on Mac OS X. This is crucial to loading and using the
114 	// OSXAdapter class.
115 	public static boolean MAC_OS_X = (System.getProperty("os.name")
116 			.toLowerCase().startsWith("mac os x"));
117 
118 	public PasProject() {
119 		initUI();
120 		initServices();
121 		initListeners();
122 		
123 	}
124 
125 	protected void initUI() {
126 		projectFrame = new PasFrame();
127 		
128 		// display a welcome message at start up for users,
129 		// eventually this can be a bean property
130 		// WelcomeGlassPane.registerFrame(projectFrame);
131 	}
132 
133 	protected void initServices() {
134 		pasServiceProvider = new PasProjectServiceProvider(this);
135 		projectFrame.setPasServiceProvider(pasServiceProvider);
136 		pasProjectNavigationPanel = new PasProjectNavigationTaskPanel(
137 				pasServiceProvider);
138 		projectFrame.setPasProjectNavigationPanel(pasProjectNavigationPanel);
139 	}
140 
141 	protected void initListeners() {
142 		// TODO should PasProject be adding the listeners for its subparts?
143 		addPropertyChangeListener(ACTIVITIES, pasProjectNavigationPanel); //$NON-NLS-1$
144 		addPropertyChangeListener("title", pasProjectNavigationPanel); //$NON-NLS-1$
145 		pasServiceProvider.getNavigationService().addNavigationListener(
146 				new NavigationListener(projectFrame, pasServiceProvider));
147 		addPropertyChangeListener("title", projectFrame); //$NON-NLS-1$
148 		registerWithMacOSX();
149 		// Application app = new Application();
150 		// app.setEnabledAboutMenu(false);
151 		// app.setEnabledPreferencesMenu(false);
152 		// app.addApplicationListener(new ApplicationAdapter() {
153 		//		   
154 		// @Override
155 		// public void handleQuit(ApplicationEvent arg0) {
156 		// ExitSessionAction exit = new ExitSessionAction(PasProject.this);
157 		// exit.actionPerformed(null);
158 		// super.handleQuit(arg0);
159 		// }
160 		//		    
161 		//		
162 		// });
163 	}
164 
165 	public PasProjectNavigationTaskPanel getPanel() {
166 		return this.pasProjectNavigationPanel;
167 	}
168 
169 	// General info dialog. The OSXAdapter calls this method when "About
170 	// OSXAdapter"
171 	// is selected from the application menu.
172 	public void about() {
173 		IssueReporter.showAboutDialog(projectFrame);
174 	}
175 
176 	// General info dialog. The OSXAdapter calls this method when "Quit
177 	// OSXAdapter"
178 	// is selected from the application menu, Cmd-Q is pressed, or "Quit" is
179 	// selected from the Dock.
180 	public void quit() {
181 		ExitSessionAction exit = new ExitSessionAction(PasProject.this);
182 		exit.actionPerformed(null);
183 	}
184 
185 	// General preferences dialog. The OSXAdapter calls this method when
186 	// "Preferences..."
187 	// is selected from the application menu.
188 	public void preferences() {
189 
190 	}
191 
192 	// Generic registration with the Mac OS X application menu. Checks the
193 	// platform, then attempts
194 	// to register with the Apple EAWT.
195 	// This method calls OSXAdapter.registerMacOSXApplication() and
196 	// OSXAdapter.enablePrefs().
197 	// See OSXAdapter.java for the signatures of these methods.
198 	public void registerWithMacOSX() {
199 		if (MAC_OS_X) {
200 			try {
201 				Class<?> osxAdapter = getClass().getClassLoader().loadClass(
202 						"org.telscenter.pas.util.OSXAdapter");
203 
204 				Class<?>[] defArgs = { PasProject.class };
205 				Method registerMethod = osxAdapter.getDeclaredMethod(
206 						"registerMacOSXApplication", defArgs);
207 				if (registerMethod != null) {
208 					Object[] args = { this };
209 					registerMethod.invoke(osxAdapter, args);
210 				}
211 				// This is slightly gross. to reflectively access methods with
212 				// boolean args,
213 				// use "boolean.class", then pass a Boolean object in as the
214 				// arg, which apparently
215 				// gets converted for you by the reflection system.
216 				defArgs[0] = boolean.class;
217 				Method prefsEnableMethod = osxAdapter.getDeclaredMethod(
218 						"enablePrefs", defArgs);
219 				if (prefsEnableMethod != null) {
220 					Object args[] = { Boolean.FALSE };
221 					prefsEnableMethod.invoke(osxAdapter, args);
222 				}
223 			} catch (NoClassDefFoundError e) {
224 				// This will be thrown first if the OSXAdapter is loaded on a
225 				// system without the EAWT
226 				// because OSXAdapter extends ApplicationAdapter in its def
227 				logger
228 						.warning("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled ("
229 								+ e + ")");
230 			} catch (ClassNotFoundException e) {
231 				// This shouldn't be reached; if there's a problem with the
232 				// OSXAdapter we should get the
233 				// above NoClassDefFoundError first.
234 				logger
235 						.warning("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled ("
236 								+ e + ")");
237 			} catch (Exception e) {
238 				logger.severe("Exception while loading the OSXAdapter:");
239 				e.printStackTrace();
240 			}
241 		}
242 	}
243 
244 	public String getAuthorNotes() {
245 		return authorNotes;
246 	}
247 
248 	public String getTitle() {
249 		return title;
250 	}
251 
252 	/***
253 	 * If this is set to false then the frame will only be hidden not disposed.
254 	 * 
255 	 * @param exitOnClose
256 	 */
257 	public void setExitOnClose(boolean exitOnClose) {
258 		this.exitOnClose = exitOnClose;
259 	}
260 
261 	public boolean getExitOnClose() {
262 		return this.exitOnClose;
263 	}
264 
265 	@Override
266 	protected void registerDesiredServices(BeanContextServices bcs) {
267 		pasServiceProvider.register(bcs);
268 	}
269 
270 	WindowListener windowListener = new WindowAdapter() {
271 		@Override
272 		public void windowClosing(WindowEvent e) {
273 			ExitSessionAction exit = new ExitSessionAction(PasProject.this);
274 			exit.actionPerformed(null);
275 		}
276 	};
277 
278 	SessionEventListener sessionListener = new SessionEventListener() {
279 
280 		public void sessionInitiated(SessionEvent e) {
281 
282 			initiatePasProject();
283 			sessionTopic = sessionService.getSessionId().toString();
284 			assert sessionTopic != null;
285 			// FIXME Mantaray is having trouble over JWS
286 			// notifyOfThisSession();
287 			// initiateNavigationPublisher();
288 		}
289 
290 		public void sessionStarted(SessionEvent e) {
291 			// save to PDF and exit if indicated on passed-in parameter
292 			// generateReportOnly is true
293 			String generateReportOnlyStr = sessionService.getProperty(
294 					"generateReportOnly", null);
295 			// generate xml representation of prompts of all questions that are gradeable
296 			String generateCurnitMapOnlyStr = sessionService.getProperty(
297 					"generateCurnitMapOnly", null);
298 			if (generateReportOnlyStr != null) {
299 				boolean generateReportOnly = Boolean.valueOf(
300 						generateReportOnlyStr).booleanValue();
301 				if (generateReportOnly) {
302 					
303 					//if there is an annotation service that means there are annotations or
304 					//annotation potiental
305 					boolean withAnnotations = false;
306 					if( getAnnotationService() != null ) {
307 						withAnnotations = true;
308 					}
309 					GeneratePDFAction action = new GeneratePDFAction(null,getTitle(),PasProject.this);
310 					String filePath = System.getProperty("user.home")
311 							+ System.getProperty("file.separator")
312 							+ action.generateFileName() + ".pdf";
313 					boolean pdfCreated = action.doPDF(filePath);
314 					if (!pdfCreated) {
315 						System.err.println("PDF was not created");
316 						System.err.println("filepath: " + filePath);
317 						System.exit(1);
318 					}
319 					System.out.println("student work saved successfuly to: "
320 							+ filePath);
321 					System.exit(0);
322 				}
323 			} else if (generateCurnitMapOnlyStr != null) {
324 				boolean generateCurnitMapOnly = Boolean.valueOf(
325 						generateCurnitMapOnlyStr).booleanValue();
326 				if (generateCurnitMapOnly) {
327 					String filePath;
328 					// TODO Come up with a better naming scheme
329 					try {
330 						filePath = File.createTempFile("curnitmap", "xml", new File(System.getProperty("user.home"))).toString();
331 					} catch (IOException ex) {
332 						filePath = System.getProperty("user.home")
333 							+ System.getProperty("file.separator")
334 							+ "curnitmap" + ".xml";
335 					}
336 					boolean isCurnitMapCreated = createCurnitMap(filePath);
337 					if (!isCurnitMapCreated) {
338 						System.err.println("CurnitMap was not created");
339 						System.err.println("filepath: " + filePath);
340 						System.exit(1);
341 					}					
342 					System.out.println("CurnitMap saved successfuly to: "
343 							+ filePath);
344 					System.exit(0);					
345 				}
346 			}
347 
348 			projectFrame.showFrame();
349 			
350 			// Only make the curnit map if we are inside of a Pod
351 			if(isInPod()){
352 				createCurnitMap();
353 			}
354 			ISock<String> sock = getNavigationLogSock();
355 			if (sock != null) {
356 				sock.add(getItemLog("project_open", PasProject.this));
357 			}
358 			if (showFirstStepOnLoad())
359 				projectFrame.showFirstStep();
360 
361 		}
362 
363 		public void sessionStopped(SessionEvent e) {
364 			ISock<String> sock = getNavigationLogSock();
365 			if (sock != null) {
366 				sock.add(getItemLog("project_close", PasProject.this));
367 			}
368 		}
369 
370 	};
371 
372 	/***
373 	 * @see java.beans.beancontext.BeanContextChildSupport#setBeanContext(java.beans.beancontext.BeanContext)
374 	 */
375 	@Override
376 	public synchronized void setBeanContext(BeanContext bc)
377 			throws PropertyVetoException {
378 		// TODO Auto-generated method stub
379 		super.setBeanContext(bc);
380 		
381 		// If we aren't in a Pod then our children's bean context won't be set so we need
382 		// to set it ourselves.
383 		if(!isInPod()){
384 			for (PasActivity activity : activitiesList) {
385 				bc.add(activity);
386 			}
387 		}
388 	}
389 	
390 	/***
391 	 * This method can be called more than once in the authoring runtime, when
392 	 * the preview is shown more than once.
393 	 * 
394 	 * @param bcs
395 	 * @param serviceClass
396 	 * @param project
397 	 */
398 	@Override
399 	@SuppressWarnings("unchecked")
400 	protected void consumeService(BeanContextServices bcs, Class serviceClass) {
401 		if (serviceClass == SessionService.class) {
402 			try {
403 				sessionService = (SessionService) bcs.getService(this, this,
404 						SessionService.class, this, this);
405 
406 				projectFrame
407 						.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
408 				projectFrame.removeWindowListener(windowListener);
409 				projectFrame.addWindowListener(windowListener);
410 
411 				sessionService.addSessionEventListener(sessionListener);
412 				projectFrame.setSessionService(sessionService);
413 				pasProjectNavigationPanel.setUserNames(projectFrame
414 						.getUserDisplayNames());
415 				pasProjectNavigationPanel.setProjectName(this.getTitle());
416 			} catch (TooManyListenersException e1) {
417 				// TODO Auto-generated catch block
418 				logger
419 						.severe("BeanContextServices, Class -  : exception: " + e1); //$NON-NLS-1$
420 			}
421 		}
422 		if (serviceClass == AgentService.class) {
423 			try {
424 				final AgentService p = (AgentService) bcs.getService(this,
425 						this, AgentService.class, this, this);
426 				agentService = p;
427 			} catch (TooManyListenersException e) {
428 				e.printStackTrace();
429 			}
430 		}
431 		if (serviceClass == AnnotationService.class) {
432 			try {
433 				final AnnotationService aService = (AnnotationService) bcs
434 						.getService(this, this, AnnotationService.class, this,
435 								this);
436 				annotationService = aService;
437 			} catch (TooManyListenersException e) {
438 				e.printStackTrace();
439 			}
440 		}
441 	}
442 
443 	
444 	/***
445 	 * Updates steps with any properties as nessecary.
446 	 * 
447 	 */
448 	protected void updateProjectProperties() {
449 		
450 		if( this.projectProperties == null )
451 			return;
452 		
453 		//cycle through all activities and steps
454 		for (PasActivity ac : activitiesList) {
455 			PasStep[] steps = ac.getSteps();
456 			for (int i = 0; i < steps.length; i++) {
457 				PasStep step = steps[i];
458 				
459 				if( step instanceof Assessment) {
460 					//for injecting starter prompts
461 					Boolean value = (Boolean) this.getProjectProperties().get("org.telscenter.assmtpromptinject");
462 					if( value != null)
463 						((Assessment)step).setInjectPrompt(value.booleanValue());
464 				}// if
465 				
466 			}// for
467 		}// for
468 		
469 		
470 	}
471 	
472 	
473 	protected void initiatePasProject() {
474 		
475 		if( this.projectProperties != null)
476 			this.updateProjectProperties();
477 		
478 		if (PasProject.this.isDesignTime())
479 			throw new RuntimeException(
480 					"initiatePasProject() should not be called in design-time, right?"); //$NON-NLS-1$
481 
482 		
483 		// gather all steps
484 		List<PasStep> allSteps = new ArrayList<PasStep>();
485 		for (PasActivity ac : activitiesList) {
486 			PasStep[] steps = ac.getSteps();
487 			for (int i = 0; i < steps.length; i++) {
488 				PasStep step = steps[i];
489 				allSteps.add(step);
490 			}
491 		}
492 
493 		// create monitor
494 		ProgressMonitor initiationMonitor = new ProgressMonitor(
495 				null,
496 				Messages.getString("PasProject.PROGRESS_MONITOR_TITLE"), Messages.getString("PasProject.Initiating"), 0, allSteps.size()); //$NON-NLS-1$ //$NON-NLS-2$
497 		initiationMonitor.setMillisToDecideToPopup(0);
498 
499 		// notify each step
500 		for (int i = 0; i < allSteps.size(); i++) {
501 			PasStep step = allSteps.get(i);
502 			long start = System.currentTimeMillis();
503 			try {
504 				step.beforeSessionStart();
505 			} catch (RuntimeException e) {
506 				logger.severe("exception: " + e); //$NON-NLS-1$
507 				logger
508 						.severe(this
509 								+ Messages
510 										.getString("PasProject.this_failed_to_initialize") + " -  : exception: " + e); //$NON-NLS-1$ //$NON-NLS-2$ 
511 			}
512 			long stop = System.currentTimeMillis();
513 			logger
514 					.info(Messages.getString("PasProject.initializing") + step + Messages.getString("PasProject.took") + (stop - start) + Messages.getString("PasProject.milliseconds")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$			
515 			initiationMonitor.setProgress(i + 1);
516 		}
517 
518 	}
519 
520 	public PasActivity[] getActivities() {
521 		PasActivity[] beans = new PasActivity[activitiesList.size()];
522 		return activitiesList.toArray(beans);
523 	}
524 
525 	public void remove(PasActivity act) {
526 		activitiesList.remove(act);
527 	}
528 
529 	/***
530 	 * @param i
531 	 * @return
532 	 */
533 	public PasActivity getActivities(int i) {
534 		return activitiesList.get(i);
535 	}
536 
537 	public void setActivities(int index, PasActivity bean) {
538 		if (bean == null)
539 			throw new IllegalArgumentException(Messages
540 					.getString("PasProject.NO_NULL")); //$NON-NLS-1$
541 		
542 		PasActivity old = (activitiesList.size() <= index) ? null : activitiesList.get(index);
543 
544 		// expand array as necessary
545 		while (index >= activitiesList.size())
546 			activitiesList.add(null);
547 		activitiesList.set(index, bean);
548 
549 		pcSupport.fireIndexedPropertyChange(
550 				ACTIVITIES, index, old, getActivities()); //$NON-NLS-1$
551 	}
552 
553 	public void setActivities(PasActivity[] beans) {
554 		PasActivity[] oldBeans = getActivities();
555 		activitiesList = new ArrayList<PasActivity>(beans.length);
556 		for (int i = 0; i < beans.length; i++) {
557 			activitiesList.add(beans[i]);
558 		}
559 		firePropertyChange(ACTIVITIES, oldBeans, beans); //$NON-NLS-1$
560 	}
561 
562 	public void setAuthorNotes(String authorNotes) {
563 		Object old = this.authorNotes;
564 		this.authorNotes = authorNotes;
565 		firePropertyChange("authorNotes", old, authorNotes); //$NON-NLS-1$
566 	}
567 
568 	public void setTitle(String title) {
569 		Object old = this.title;
570 		this.title = title;
571 		pasProjectNavigationPanel.setProjectName(title);
572 		firePropertyChange("title", old, title); //$NON-NLS-1$
573 	}
574 
575 	public PasFrame getProjectFrame() {
576 		return projectFrame;
577 	}
578 
579 	public void setProjectFrame(PasFrame pasFrame) {
580 		this.projectFrame = pasFrame;
581 	}
582 
583 	// added so the bean editor can detect which properties are bound
584 	public void addPropertyChangeListener(PropertyChangeListener listener) {
585 		pcSupport.addPropertyChangeListener(listener);
586 	}
587 
588 	// added so the bean editor can detect which properties are bound
589 	public void removePropertyChangeListener(PropertyChangeListener listener) {
590 		pcSupport.removePropertyChangeListener(listener);
591 	}
592 
593 	/***
594 	 * @return the sessionService
595 	 */
596 	public SessionService getSessionService() {
597 		return sessionService;
598 	}
599 
600 	/***
601 	 * @param sessionService
602 	 *            the sessionService to set
603 	 */
604 	public void setSessionService(SessionService sessionService) {
605 		this.sessionService = sessionService;
606 	}
607 
608 	/***
609 	 * @param navigationLogRim
610 	 *            the navigationLogRim to set
611 	 */
612 	public void setNavigationLogRim(Rim<String> navigationLogRim) {
613 		this.navigationLogRim = navigationLogRim;
614 	}
615 
616 	/***
617 	 * @return the navigationLogRim
618 	 */
619 	public Rim<String> getNavigationLogRim() {
620 		return navigationLogRim;
621 	}
622 
623 	/***
624 	 * @param sessionStateRim
625 	 *            the sessionStateRim to set
626 	 */
627 	public void setSessionStateRim(Rim<String> sessionStateRim) {
628 		this.sessionStateRim = sessionStateRim;
629 	}
630 
631 	/***
632 	 * @return the sessionStateRim
633 	 */
634 	public Rim<String> getSessionStateRim() {
635 		return sessionStateRim;
636 	}
637 
638 	/***
639 	 * @param curnitMapRim
640 	 *            the curnitMapRim to set
641 	 */
642 	public void setCurnitMapRim(Rim<String> curnitMapRim) {
643 		this.curnitMapRim = curnitMapRim;
644 	}
645 
646 	/***
647 	 * @return the curnitMapRim
648 	 */
649 	public Rim<String> getCurnitMapRim() {
650 		return curnitMapRim;
651 	}
652 
653 	protected ISock<String> getRimSock(Rim<String> rim) {
654 		ISock<String> sock = null;
655 		if (rim != null) {
656 			try {
657 				IAgent agent = agentService.getAgentsInRole(Role.RUN_WORKGROUP)
658 						.getSingle();
659 				sock = agentService.getSock(rim, agent);
660 			} catch (MismatchedAgentSetSizeException e) {
661 				// TODO Auto-generated catch block
662 				e.printStackTrace();
663 			} catch (UnsupportedRimShapeException e) {
664 				// TODO Auto-generated catch block
665 				e.printStackTrace();
666 			}
667 		}
668 		return sock;
669 	}
670 
671 	/***
672 	 * @return
673 	 */
674 	public ISock<String> getNavigationLogSock() {
675 		if (navigationLogSock == null) {
676 			navigationLogSock = getRimSock(navigationLogRim);
677 		}
678 		return navigationLogSock;
679 	}
680 
681 	/***
682 	 * @return
683 	 */
684 	public ISock<String> getSessionStateSock() {
685 		if (sessionStateSock == null) {
686 			sessionStateSock = getRimSock(sessionStateRim);
687 		}
688 		return sessionStateSock;
689 	}
690 
691 	/***
692 	 * @return
693 	 */
694 	public ISock<String> getCurnitMapSock() {
695 		if (curnitMapSock == null) {
696 			curnitMapSock = getRimSock(curnitMapRim);
697 		}
698 		return curnitMapSock;
699 	}
700 
701 	public String getItemUUID(BeanContextChildSupport item) {
702 		if (item != null) {
703 			Object beanContext = item.getBeanContext();
704 			if (beanContext instanceof Pod) {
705 				Pod pod = (Pod) beanContext;
706 				return pod.getPodId().toString();
707 			}
708 		}
709 		return null;
710 	}
711 
712 	/***
713 	 * @param step
714 	 * @return
715 	 */
716 	public String getItemLog(String name, BeanContextChildSupport item) {
717 		Element itemLog = domFactory.element(name);
718 		Document document = domFactory.document(itemLog);
719 		String id = getItemUUID(item);
720 		itemLog.setAttribute("podUUID", id == null ? "null" : id);
721 		return outputter.outputString(document);
722 	}
723 	
724 	protected void createStepMap(Element stepElement,
725 			PasStep step) {
726 		if (step instanceof IWorkReporter) {
727 			Map<String, String> entityToPromptMap = ((IWorkReporter) step).getEntityToPromptMap();
728 						
729 			// add possibleScore...In the future, this should be set from the authoring tool
730 			Integer possibleScore = ((IWorkReporter)step).getPossibleScore();
731 			
732 			if( possibleScore != null )
733 				stepElement.setAttribute("possibleScore", possibleScore.toString());
734 			if (possibleScore == null) {
735 				possibleScore = 20;
736 			}			
737 			
738 			((IWorkReporter)step).setPossibleScore(20);
739 			
740 			stepElement.setAttribute("possibleScore", possibleScore.toString());
741 			
742 			if( entityToPromptMap != null ) {
743 				for (String entityName : entityToPromptMap.keySet()) {
744 					String prompt = entityToPromptMap.get(entityName);
745 					Element rimElement = domFactory.element("rim");
746 					rimElement.setAttribute("rimname", entityName);
747 					rimElement.setAttribute("prompt", prompt);
748 					stepElement.addContent(rimElement);
749 				}
750 			}
751 		} else {
752 			// do nothing for now...non-gradable steps's rim names are not
753 			// needed for grading
754 		}
755 	}
756 
757 	protected void createActivityMap(Element activityElement,
758 			PasActivity activity) {
759 		PasStep[] steps = activity.getSteps();
760 		for (int i = 0; i < steps.length; i++) {
761 			PasStep step = steps[i];
762 			String id = getItemUUID(step);
763 			itemParentMap.put(id, activity);
764 			Element stepElement = domFactory.element("step");
765 			stepElement.setAttribute("podUUID", id);
766 			stepElement.setAttribute("title", step.getTitle());
767 			stepElement.setAttribute("number", "" + i);
768 			stepElement.setAttribute("type", step.getType());
769 			stepElement.setAttribute("classname", step.getClass().getName());
770 			activityElement.addContent(stepElement);
771 			createStepMap(stepElement, step);
772 		}
773 	}
774 
775 	protected void createProjectMap(Element projectElement, PasProject project) {
776 		PasActivity[] activities = getActivities();
777 		for (int i = 0; i < activities.length; i++) {
778 			PasActivity activity = activities[i];
779 			String id = getItemUUID(activity);
780 			itemParentMap.put(id, project);
781 			Element activityElement = domFactory.element("activity");
782 			activityElement.setAttribute("podUUID", id);
783 			activityElement.setAttribute("title", activity.getTitle());
784 			activityElement.setAttribute("number", "" + i);
785 			projectElement.addContent(activityElement);
786 			createActivityMap(activityElement, activity);
787 		}
788 	}
789 
790 	public void createCurnitMap() {
791 		if (document != null) {
792 			return;
793 		}
794 		String id = getItemUUID(this);
795 		Element root = domFactory.element("pas_curnit_map");
796 		document = domFactory.document(root);
797 		Element projectElement = domFactory.element("project");
798 		projectElement.setAttribute("podUUID", id);
799 		projectElement.setAttribute("title", getTitle());
800 		root.addContent(projectElement);
801 		createProjectMap(projectElement, this);
802 		ISock<String> sock = getCurnitMapSock();
803 		if (sock != null) {
804 			sock.add(outputter.outputString(document));
805 		}
806 	}
807 	
808 	/***
809 	 * Overloaded createCurnitMap method that output the curnitmap to provided filepath
810 	 * 
811 	 * @param filepath path to output the curnitmap
812 	 * @return true iff the curnitmap was successfully outputted
813 	 */
814 	public boolean createCurnitMap(String filepath) {
815 		createCurnitMap();
816 		try {
817 			File file = new File(filepath);
818 			FileOutputStream fos = new FileOutputStream(file);
819 			outputter.output(document, fos);
820 		} catch (IOException e) {
821 			return false;
822 		}
823 		return true;
824 	}
825 
826 	public BeanContextChildSupport getItemParent(BeanContextChildSupport item) {
827 		return itemParentMap.get(getItemUUID(item));
828 	}
829 
830 	/***
831 	 * @return the showFirstStepOnLoad
832 	 */
833 	public boolean showFirstStepOnLoad() {
834 		return showFirstStepOnLoad;
835 	}
836 
837 	/***
838 	 * @param showFirstStepOnLoad
839 	 *            the showFirstStepOnLoad to set
840 	 */
841 	public void setShowFirstStepOnLoad(boolean showFirstStepOnLoad) {
842 		this.showFirstStepOnLoad = showFirstStepOnLoad;
843 	}
844 
845 	/***
846 	 * @return the autoNumbering
847 	 */
848 	public boolean isAutoNumbering() {
849 		return autoNumbering;
850 	}
851 
852 	/***
853 	 * @param autoNumbering
854 	 *            the autoNumbering to set
855 	 */
856 	public void setAutoNumbering(boolean autoNumbering) {
857 		Object old = this.autoNumbering;
858 		this.autoNumbering = autoNumbering;
859 		firePropertyChange("autoNumbering", old, autoNumbering); //$NON-NLS-1$
860 	}
861 
862 	/***
863 	 * @return the annotationService
864 	 */
865 	public AnnotationService getAnnotationService() {
866 		return annotationService;
867 	}
868 
869 	/***
870 	 * @param annotationService
871 	 *            the annotationService to set
872 	 */
873 	public void setAnnotationService(AnnotationService annotationService) {
874 		this.annotationService = annotationService;
875 	}
876 
877 	public HashMap getProjectProperties() {
878 		return projectProperties;
879 	}
880 
881 	public void setProjectProperties(HashMap projectProperties) {
882 		this.projectProperties = projectProperties;
883 	}
884 
885 	/***
886 	 * @param authoringMode the authoringMode to set
887 	 */
888 	public void setAuthoringMode(boolean authoringMode) {
889 		this.authoringMode = authoringMode;
890 	}
891 
892 	/***
893 	 * @return the authoringMode
894 	 */
895 	public boolean isAuthoringMode() {
896 		return authoringMode;
897 	}
898 }