View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  package org.kuali.student.common.assembly.dictionary;
16  
17  import java.util.ArrayList;
18  import java.util.Collections;
19  import java.util.List;
20  import java.util.Set;
21  
22  import org.kuali.student.common.assembly.data.ConstraintMetadata;
23  import org.kuali.student.common.assembly.data.Data;
24  import org.kuali.student.common.assembly.data.LookupMetadata;
25  import org.kuali.student.common.assembly.data.LookupParamMetadata;
26  import org.kuali.student.common.assembly.data.Metadata;
27  import org.kuali.student.common.assembly.data.MetadataInterrogator;
28  
29  public class MetadataFormatter {
30  
31  	private StringBuilder builder = new StringBuilder(5000);
32  	private Metadata structureMetadata;
33  	private String type;
34  	private String rowSeperator = "\n";
35  	private String colSeperator = "|";
36  	private String structureName;
37  	private int level;
38  //	private Map<String, Metadata> subStructuresToProcess = new LinkedHashMap<String, Metadata>();
39  	private Set<Metadata> structuresAlreadyProcessed;
40  	private MetadataFormatter parent;
41  
42  	public MetadataFormatter(String structureName, Metadata structureMetadata,
43  			String type, MetadataFormatter parent,
44  			Set<Metadata> structuresAlreadyProcessed, int level) {
45  		this.structureName = structureName;
46  		this.structureMetadata = structureMetadata;
47  		this.type = type;
48  		this.parent = parent;
49  		this.structuresAlreadyProcessed = structuresAlreadyProcessed;
50  		this.level = level;
51  	}
52  
53  	public String getStructureName() {
54  		return structureName;
55  	}
56  
57  	public MetadataFormatter getParent() {
58  		return parent;
59  	}
60  
61  	public String getRowSeperator() {
62  		return rowSeperator;
63  	}
64  
65  	public void setRowSeperator(String rowSeperator) {
66  		this.rowSeperator = rowSeperator;
67  	}
68  
69  	public String getColSeparator() {
70  		return colSeperator;
71  	}
72  
73  	public void setColSeparator(String separator) {
74  		this.colSeperator = separator;
75  	}
76  
77  	private String pad(String str, int size) {
78  		StringBuilder padStr = new StringBuilder(size);
79  		padStr.append(str);
80  		while (padStr.length() < size) {
81  			padStr.append(' ');
82  		}
83  		return padStr.toString();
84  	}
85  
86  	public String formatForWiki() {
87  		if (!this.structuresAlreadyProcessed.add(structureMetadata)) {
88  			return "";
89  		}
90  		if (level == 1) {
91  			builder.append(rowSeperator);
92  			// builder.append
93  			// ("======= start dump of object structure definition ========");
94  			builder.append(rowSeperator);
95  			String header = "h1. " + this.calcSimpleName(structureName);
96  			if (type != null) {
97  				header = "h2. " + type;
98  			}
99  			builder.append(header);
100 			builder.append("{anchor:" + structureName + "}");
101 			builder.append(rowSeperator);
102 			builder.append("The object key is " + structureName);
103 			builder.append(rowSeperator);
104 			builder.append("The type is " + type);
105 			builder.append(rowSeperator);
106 			builder.append(colSeperator);
107 			builder.append(colSeperator);
108 			builder.append("Field");
109 			builder.append(colSeperator);
110 			builder.append(colSeperator);
111 			builder.append("Required?");
112 			builder.append(colSeperator);
113 			builder.append(colSeperator);
114 			builder.append("DataType");
115 			builder.append(colSeperator);
116 			builder.append(colSeperator);
117 			builder.append("Length");
118 			builder.append(colSeperator);
119 			builder.append(colSeperator);
120 			builder.append("Dynamic");
121 			builder.append(colSeperator);
122 			builder.append(colSeperator);
123 			builder.append("Default");
124 			builder.append(colSeperator);
125 			builder.append(colSeperator);
126 			builder.append("Repeats?");
127 			builder.append(colSeperator);
128 			builder.append(colSeperator);
129 			builder.append("Valid Characters");
130 			builder.append(colSeperator);
131 			builder.append(colSeperator);
132 			builder.append("Lookup Widget");
133 			builder.append(colSeperator);
134 			builder.append(colSeperator);
135 			builder.append("Lookup");
136 			builder.append(colSeperator);
137 			builder.append(colSeperator);
138 			builder.append(rowSeperator);
139 		}
140 
141 		List<String> keys = new ArrayList<String>();
142 		keys.addAll(structureMetadata.getProperties().keySet());
143 		Collections.sort(keys);
144 		for (String key : keys) {
145    if (key.equalsIgnoreCase ("_runtimeData"))
146    {
147     continue;
148    }
149 			Metadata fieldMeta = structureMetadata.getProperties().get(key);
150 			builder.append(colSeperator);
151 			builder.append(calcFieldInfo (key, fieldMeta));
152 			builder.append(colSeperator);
153 			builder.append(pad(calcRequired(fieldMeta), 10));
154 			builder.append(colSeperator);
155 			builder.append(pad(calcDataType(fieldMeta), 25));
156 			builder.append(colSeperator);
157 			builder.append(pad(calcLength(fieldMeta), 15));
158 			builder.append(colSeperator);
159 			builder.append(pad(calcDynamic(fieldMeta), 7));
160 			builder.append(colSeperator);
161 			builder.append(pad(calcDefaultValue(fieldMeta), 15));
162 			builder.append(colSeperator);
163 			builder.append(calcRepeating(fieldMeta));
164 			builder.append(colSeperator);
165 			builder.append(calcValidChars(fieldMeta));
166 			builder.append(colSeperator);
167 			builder.append(calcWidget(fieldMeta));
168 			builder.append(colSeperator);
169 			builder.append(calcLookup(fieldMeta));
170 			builder.append(colSeperator);
171 			builder.append(rowSeperator);
172 			if (fieldMeta.getDataType().equals(Data.DataType.DATA)
173 					|| fieldMeta.getDataType().equals(Data.DataType.LIST)) {
174 				if (fieldMeta.getProperties() == null) {
175 					throw new IllegalArgumentException(
176 							fieldMeta.getName()
177 									+ " is DATA but does not have a sub-structure defined");
178 				}
179 				MetadataFormatter formatter = new MetadataFormatter(key,
180 						fieldMeta, null, this, structuresAlreadyProcessed,
181 						level + 1);
182 				builder.append(formatter.formatForWiki());
183 			}
184 		}
185 
186 		return builder.toString();
187 	}
188 
189 	private String calcDataType(Metadata fieldMeta) {
190 		if (fieldMeta.getDataType().equals(Data.DataType.LIST)) {
191 			StringBuilder type = new StringBuilder();
192 			type.append("LIST of ");
193 			String comma = "";
194 			for (String key : fieldMeta.getProperties().keySet()) {
195 				type.append(comma);
196 				type.append(fieldMeta.getProperties().get(key).getDataType()
197 						.toString());
198 				comma = ", ";
199 			}
200 			return type.toString();
201 		}
202 		return fieldMeta.getDataType().toString();
203 	}
204 
205 	private String calcDefaultValue(Metadata fieldMeta) {
206 		if (fieldMeta.getDefaultValue() != null) {
207 			return fieldMeta.getDefaultValue().toString();
208 		}
209 		return " ";
210 	}
211 
212 	private String calcDynamic(Metadata meta) {
213 		if (meta.isDynamic()) {
214 			return "dynamic";
215 		}
216 		return " ";
217 	}
218 
219  private String calcFieldInfo (String key, Metadata fieldMeta)
220  {
221   StringBuilder bldr = new StringBuilder (40);
222   bldr.append (pad(calcFullyQualifiedFieldName(key), 30));
223   if (fieldMeta.getLabelKey () != null)
224   {
225    bldr.append ("\\\\\n");
226    bldr.append ("Label: ");
227    bldr.append (fieldMeta.getLabelKey ());
228   }
229   return bldr.toString ();
230  }
231 
232 	public String calcFullyQualifiedFieldName(String fieldName) {
233 		if (parent == null) {
234 			return escapeWiki(fieldName);
235 		}
236 		return parent.calcFullyQualifiedFieldName(structureName) + "."
237 				+ escapeWiki(fieldName);
238 	}
239 
240 	private String calcSimpleName(String name) {
241 		if (name.lastIndexOf(".") != -1) {
242 			name = name.substring(name.lastIndexOf(".") + 1);
243 		}
244 		return name;
245 	}
246 
247 	private String calcNotSoSimpleName(String name) {
248 		if (name.lastIndexOf(".") == -1) {
249 			return name;
250 		}
251 		String simpleName = calcSimpleName(name);
252 		String fieldName = calcSimpleName(name.substring(0, name.length()
253 				- simpleName.length() - 1));
254 		return fieldName + "." + simpleName;
255 	}
256 
257 	private String calcRequired(Metadata fieldMeta) {
258 		for (ConstraintMetadata cons : fieldMeta.getConstraints()) {
259 			if (cons.getMaxOccurs() != null) {
260 				if (cons.getMaxOccurs() == 0) {
261 					return "NOT USED";
262 				}
263 			}
264 
265 			if (cons.getMinOccurs() != null) {
266 				if (cons.getMinOccurs() >= 1) {
267 					return "required";
268 				}
269 			}
270 		}
271 		return " ";
272 		// return "optional";
273 	}
274 
275 	private static final String LINK_TO_DEFINITIONS = "KULSTG:Formatted View of Base Dictionary#Valid Character Definitions";
276 
277 	private String calcValidChars(Metadata fieldMeta) {
278 		for (ConstraintMetadata cons : fieldMeta.getConstraints()) {
279 			if (cons.getValidChars() == null) {
280 				continue;
281 			}
282 			String validChars = escapeWiki(cons.getValidChars());
283 			String descr = "[" + "See" + "|" + LINK_TO_DEFINITIONS + "]"
284 					+ "\\\\\n" + validChars;
285 			return descr;
286 		}
287 		return " ";
288 	}
289 
290 	private String escapeWiki(String str) {
291 		StringBuilder bldr = new StringBuilder(str.length());
292 		boolean precededByBackSlash = false;
293 		for (int i = 0; i < str.length(); i++) {
294 			char c = str.charAt(i);
295 			switch (c) {
296 			case '\\':
297 			case '[':
298 			case '*':
299 			case ']':
300 			case '|':
301 				if (!precededByBackSlash) {
302 					bldr.append('\\');
303 				}
304 				break;
305 			default:
306 				break;
307 			}
308 			bldr.append(c);
309 			if (c == '\\') {
310 				precededByBackSlash = true;
311 			} else {
312 				precededByBackSlash = false;
313 			}
314 		}
315 		return bldr.toString();
316 	}
317 
318 	private String calcWidget(Metadata fieldMeta) {
319 		StringBuilder bldr = new StringBuilder();
320 		String comma = "";
321 		if (!fieldMeta.isCanEdit()) {
322 			bldr.append(comma);
323 			bldr.append("not editable");
324 			comma = ", ";
325 		}
326 		if (!fieldMeta.isCanView()) {
327 			bldr.append(comma);
328 			bldr.append("not viewable");
329 			comma = ", ";
330 		}
331 		if (!fieldMeta.isCanUnmask()) {
332 			bldr.append(comma);
333 			bldr.append("Not unmaskable");
334 			comma = ", ";
335 		}
336 		if (fieldMeta.getInitialLookup() != null) {
337 			bldr.append(comma);
338 			bldr.append(fieldMeta.getInitialLookup().getWidget());
339 			comma = ", ";
340 		}
341 		if (bldr.length() == 0) {
342 			bldr.append(" ");
343 		}
344 		return bldr.toString();
345 	}
346 
347 	private String calcLookup(Metadata fieldMeta) {
348   StringBuilder bldr = new StringBuilder ();
349 		if (fieldMeta.getInitialLookup() != null) {
350 		 bldr.append (calcLookup (fieldMeta.getInitialLookup ()));
351 		}
352    
353   if (fieldMeta.getAdditionalLookups () != null) {
354    if (fieldMeta.getAdditionalLookups ().size () > 0) {
355     if (fieldMeta.getInitialLookup() == null) {
356 		   bldr.append ("No initial lookup but...");
357 		  }
358 		  bldr.append("\\\\");
359 		  bldr.append("\n");   
360 		  bldr.append("\\\\");
361 		  bldr.append("\n");
362     bldr.append ("Additional Lookups:");
363 		  bldr.append("\\\\");
364 		  bldr.append("\n");
365    }
366    for (LookupMetadata lm : fieldMeta.getAdditionalLookups ())  {
367 		  bldr.append("\\\\");
368   		bldr.append("\n");
369     bldr.append (calcLookup (lm));
370     bldr.append("\\\\");
371 		  bldr.append("\n");
372    }  
373   }
374   if (bldr.length () == 0)
375   {
376    bldr.append (" ");
377   }
378   return bldr.toString ();
379  }
380 
381  private String calcLookup(LookupMetadata lm) {
382 		StringBuilder bldr = new StringBuilder();
383 		bldr.append(lm.getId());
384 //  if (lm.getUsage () != null) {
385 //   bldr.append (" usage " + lm.getUsage ());
386 //  }
387 		// this is the search description not the lookup description
388 		bldr.append (" - ");
389 		bldr.append (lm.getName());
390   bldr.append (" " + lm.getWidget ());
391   String and = " with option ";
392   if (lm.getWidgetOptions () != null) {
393    for (LookupMetadata.WidgetOption wo: lm.getWidgetOptions ().keySet ())
394    {
395     bldr.append (" and ");
396     bldr.append (wo);
397     bldr.append ("=");
398     bldr.append (lm.getWidgetOptions ().get (wo));
399    }
400   }
401 		and = "";
402 		bldr.append("\\\\\n");
403 		bldr.append("Implemented using search: ");
404 		String searchPage = calcWikiSearchPage(lm.getSearchTypeId());
405 		bldr.append("[" + lm.getSearchTypeId() + "|" + searchPage + "#"
406 				+ lm.getSearchTypeId() + "]");
407 		List<LookupParamMetadata> configuredParameters = filterConfiguredParams(lm
408 				.getParams());
409 		if (configuredParameters.size() > 0) {
410 			bldr.append("\\\\");
411 			bldr.append("\n");
412 			bldr.append(" where ");
413 			and = "";
414 			for (LookupParamMetadata param : configuredParameters) {
415 				bldr.append(and);
416 				and = " and ";
417 				bldr.append(param.getName());
418     bldr.append (" (" + param.getDataType () + ") ");
419 				bldr.append("=");
420 				if (param.getDefaultValueString() != null) {
421 					bldr.append(param.getDefaultValueString());
422 				}
423 				if (param.getDefaultValueList() != null) {
424 					String comma = "";
425 					for (String defValue : param.getDefaultValueList()) {
426 						bldr.append(comma);
427 						comma = ", ";
428 						bldr.append(defValue);
429 					}
430 				}
431 			}
432   }
433   List<LookupParamMetadata> userEnterableParameters = this.filterUserEnterableParams (lm
434 				.getParams());
435 		if (userEnterableParameters.size() > 0) {
436 			bldr.append("\\\\");
437 			bldr.append("\n");
438 			bldr.append(" and the user can enter: ");
439 			for (LookupParamMetadata param : userEnterableParameters) {
440 				bldr.append ("\\\\\n");
441 				bldr.append(param.getName());
442     bldr.append (" (" + param.getDataType () + ")");
443     if (param.getWidget () != null) {
444 				 bldr.append(" using widget ");
445      bldr.append (param.getWidget ());
446     }
447 				if (param.getDefaultValueString() != null) {
448 					bldr.append("defaulted to " + param.getDefaultValueString());
449 				}
450 				if (param.getDefaultValueList() != null) {
451 					String comma = "defaulted to ";
452 					for (String defValue : param.getDefaultValueList()) {
453 						bldr.append(comma);
454 						comma = ", ";
455 						bldr.append(defValue);
456 					}
457 				}
458     if (param.getChildLookup () != null)
459     {
460 			  bldr.append("\\\\");
461 			  bldr.append("\n");
462      bldr.append ("using a child lookup: ");
463 			  bldr.append("\\\\");
464 			  bldr.append("\n");
465      bldr.append (calcLookup (param.getChildLookup ()));
466     }
467    }
468 		}
469 		return bldr.toString();
470  }
471 
472 	private static final String PAGE_PREFIX = "Formatted View of ";
473 	private static final String PAGE_SUFFIX = " Searches";
474 
475 	private String calcWikiSearchPage(String searchType) {
476 		return PAGE_PREFIX + calcWikigPageAbbrev(searchType) + PAGE_SUFFIX;
477 	}
478 
479 	private String calcWikigPageAbbrev(String searchType) {
480 		if (searchType == null) {
481 			return null;
482 		}
483 		if (searchType.equals("enumeration.management.search")) {
484 			return "EM";
485 		}
486 		if (searchType.startsWith("lu.")) {
487 			return "LU";
488 		}
489 		if (searchType.startsWith("cluset.")) {
490 			return "LU";
491 		}
492 		if (searchType.startsWith("lo.")) {
493 			return "LO";
494 		}
495 		if (searchType.startsWith("lrc.")) {
496 			return "LRC";
497 		}
498 		if (searchType.startsWith("comment.")) {
499 			return "Comment";
500 		}
501 		if (searchType.startsWith("org.")) {
502 			return "Organization";
503 		}
504 		if (searchType.startsWith("atp.")) {
505 			return "ATP";
506 		}
507 		if (searchType.startsWith("person.")) {
508 			return "Person";
509 		}
510   if (searchType.startsWith("proposal.")) {
511 			return "Proposal";
512 		}
513 		throw new IllegalArgumentException("Unknown type of search: "
514 				+ searchType);
515 	}
516 
517 	private List<LookupParamMetadata> filterConfiguredParams(
518 			List<LookupParamMetadata> params) {
519 		List<LookupParamMetadata> list = new ArrayList<LookupParamMetadata>();
520 		if (params == null) {
521 			return list;
522 		}
523 		if (params.size() == 0) {
524 			return list;
525 		}
526 		for (LookupParamMetadata param : params) {
527 			if (param.getDefaultValueString() != null) {
528 				list.add(param);
529 				continue;
530 			}
531 			if (param.getDefaultValueList() != null) {
532 				list.add(param);
533     continue;
534 			}
535 		}
536 		return list;
537 	}
538 
539  	private List<LookupParamMetadata> filterUserEnterableParams (
540 			List<LookupParamMetadata> params) {
541 		List<LookupParamMetadata> list = new ArrayList<LookupParamMetadata>();
542 		if (params == null) {
543 			return list;
544 		}
545 		if (params.size() == 0) {
546 			return list;
547 		}
548 		for (LookupParamMetadata param : params) {
549 			if (param.getWriteAccess () != null) {
550     if ( ! param.getWriteAccess ().equals (Metadata.WriteAccess.NEVER)) {
551 			 	list.add(param);
552 			 	continue;
553     }
554 			}
555 		}
556 		return list;
557 	}
558 
559 	private String calcRepeating(Metadata fieldMeta) {
560 		if (!fieldMeta.getDataType().equals(Data.DataType.LIST)) {
561 			return " ";
562 		}
563 		// return "repeating";
564 		MetadataInterrogator mi = new MetadataInterrogator(fieldMeta);
565 		if (mi.getSmallestMaxOccurs() == null) {
566 			if (mi.getLargestMinOccurs() != null
567 					&& mi.getLargestMinOccurs() > 1) {
568 				return "repeating: minimum " + mi.getLargestMinOccurs()
569 						+ " times";
570 			}
571 			return "repeating: unlimited";
572 		}
573 		if (mi.getSmallestMaxOccurs() == 0) {
574 			return "NOT USED";
575 		}
576 		if (mi.getSmallestMaxOccurs() == 1) {
577 			return " ";
578 			// return "single";
579 		}
580 
581 		if (mi.getLargestMinOccurs() != null) {
582 			if (mi.getLargestMinOccurs() > 1) {
583 				return "repeating: " + mi.getLargestMinOccurs() + " to "
584 						+ mi.getSmallestMaxOccurs() + " times";
585 			}
586 		}
587 		return "repeating: maximum " + mi.getSmallestMaxOccurs() + " times";
588 	}
589 
590 	private String calcLength(Metadata fieldMeta) {
591 		MetadataInterrogator mi = new MetadataInterrogator(fieldMeta);
592 		if (mi.getSmallestMaxLength() != null) {
593 			if (mi.getLargestMinLength() != null
594 					&& mi.getLargestMinLength() != 0) {
595 				if (mi.getSmallestMaxLength() == mi.getLargestMinLength()) {
596 					return ("(must be " + mi.getSmallestMaxLength() + ")");
597 				}
598 				return "(" + mi.getLargestMinLength() + " to "
599 						+ mi.getSmallestMaxLength() + ")";
600 			}
601 			return "(up to " + mi.getSmallestMaxLength() + ")";
602 		}
603 		if (mi.getLargestMinLength() != null) {
604 			return "(over " + mi.getLargestMinLength() + ")";
605 		}
606 		return " ";
607 	}
608 }