001: /* *****************************************************************************
002: * Comment.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2007 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.js2doc;
011:
012: import java.util.*;
013: import java.util.logging.*;
014: import java.util.regex.*;
015:
016: public class Comment {
017:
018: private static Logger logger = Logger
019: .getLogger("org.openlaszlo.js2doc.Comment");
020:
021: static private final Pattern commentLineStart = Pattern
022: .compile("^\\s*/\\*((?:.)*)$");
023: static private final Pattern commentLineEnd = Pattern
024: .compile("^((?:.)*)\\*/.*$");
025:
026: static private final Pattern contentsPattern = Pattern.compile(
027: "^\\s*\\*+(.*)$", Pattern.MULTILINE);
028: static private final Pattern fieldLinePattern = Pattern
029: .compile("^\\s*@(\\w+)\\s+(.*)$");
030: static private final Pattern fieldPattern = Pattern.compile(
031: "^\\s*@(\\w+)\\s+(.*)$", Pattern.DOTALL);
032:
033: static public ArrayList extractRawComments(
034: String sourceCommentSequence) {
035:
036: String cr = System.getProperty("line.separator");
037:
038: Scanner sc = new Scanner(sourceCommentSequence);
039:
040: ArrayList results = new ArrayList();
041:
042: try {
043: while (sc.hasNextLine()) {
044: String sl = sc.nextLine();
045:
046: Matcher startMatcher = commentLineStart.matcher(sl);
047: if (startMatcher.matches()) {
048:
049: boolean inside = true;
050: String comment = "";
051: sl = startMatcher.group(1);
052:
053: do {
054: Matcher stopMatcher = commentLineEnd
055: .matcher(sl);
056: if (stopMatcher.matches()) {
057: comment += stopMatcher.group(1);
058: inside = false;
059: } else if (sc.hasNextLine()) {
060: comment += sl + cr;
061: sl = sc.nextLine();
062: } else {
063: throw new RuntimeException(
064: "Java comment not properly terminated");
065: }
066: } while (inside);
067:
068: results.add(comment);
069: }
070: }
071: } catch (RuntimeException exc) {
072:
073: }
074: return results;
075: }
076:
077: static public Comment extractJS2DocComment(String sourceComment) {
078:
079: String cr = System.getProperty("line.separator");
080:
081: Comment parsedComment = new Comment();
082:
083: if (sourceComment != null) {
084: String content = contentsPattern.matcher(sourceComment)
085: .replaceAll("$1");
086:
087: String[] lines = content.split(cr);
088:
089: int i = 0;
090: String textContent = "";
091: while (i < lines.length
092: && !fieldLinePattern.matcher(lines[i]).matches()) {
093: textContent += "\n" + lines[i];
094: i++;
095: }
096: textContent = textContent.trim();
097: if (textContent.compareTo("") != 0) {
098: parsedComment.textContent = textContent;
099: }
100:
101: while (i < lines.length) {
102: // lines[i] now matches fieldLinePattern, so it is the first line
103: // of our new field string
104: String field = lines[i];
105: i++;
106:
107: // now collect any followup lines
108: while (i < lines.length
109: && !fieldLinePattern.matcher(lines[i])
110: .matches()) {
111: field += "\n" + lines[i];
112: i++;
113: }
114:
115: // finally, extract the field name and value and add them to the doc node
116: Matcher fieldMatcher = fieldPattern.matcher(field);
117: boolean found = fieldMatcher.find();
118: if (!found)
119: logger
120: .warning("field text didn't match fieldPattern regex. Shouldn't happen");
121:
122: String fieldName = fieldMatcher.group(1);
123: String fieldValue = fieldMatcher.group(2).trim();
124:
125: if (fieldName.equals("keywords")
126: || fieldName.equals("keyword")) {
127: StringTokenizer keywordTokens = new StringTokenizer(
128: fieldValue);
129: while (keywordTokens.hasMoreTokens()) {
130: parsedComment.keywords.add(keywordTokens
131: .nextToken());
132: }
133: } else {
134: parsedComment.addField(fieldName, fieldValue);
135: }
136: }
137: }
138: return parsedComment;
139: }
140:
141: static public ArrayList extractStructuredComments(
142: String sourceCommentSequence) {
143:
144: ArrayList rawComments = extractRawComments(sourceCommentSequence);
145:
146: ArrayList processedComments = new ArrayList();
147:
148: final int n = rawComments.size();
149: for (int i = 0; i < n; i++) {
150: String this Comment = (String) rawComments.get(i);
151:
152: // see if it matches the special structured comment format
153: if (this Comment.charAt(0) == '*') {
154: // it does -- extract the contents and save them.
155: String js2docComment = this Comment.substring(1);
156: Comment c = extractJS2DocComment(js2docComment);
157: processedComments.add(c);
158: }
159: }
160:
161: return processedComments;
162: }
163:
164: static public void appendStructuredCommentsAsXML(
165: String sourceCommentSequence, org.w3c.dom.Node parentNode) {
166:
167: ArrayList structuredComments = extractStructuredComments(sourceCommentSequence);
168:
169: final int n = structuredComments.size();
170: for (int i = 0; i < n; i++) {
171: Comment this Comment = (Comment) structuredComments.get(i);
172:
173: this Comment.appendAsXML(parentNode);
174: }
175: }
176:
177: static public Comment extractLastJS2DocFromCommentSequence(
178: String sourceSequence) {
179:
180: String content = sourceSequence.trim();
181:
182: ArrayList comments = extractRawComments(content);
183:
184: // find the properly-formatted comment that is closest to the end
185: String lastComment = null;
186: final int n = comments.size();
187: for (int i = 0; i < n; i++) {
188: String this Comment = (String) comments.get(i);
189:
190: if (lastComment != null) {
191: logger
192: .fine("Skipping js2doc-formatted comment without adjacent target");
193: lastComment = null;
194: }
195:
196: // see if it matches the special js2doc format
197: if (this Comment.charAt(0) == '*') {
198: // it does -- extract the contents and save them.
199: String js2docComment = this Comment.substring(1);
200: lastComment = js2docComment;
201: }
202: }
203:
204: return extractJS2DocComment(lastComment);
205: }
206:
207: static public Comment extractFirstJS2DocFromCommentSequence(
208: String sourceSequence) {
209:
210: String content = sourceSequence.trim();
211:
212: ArrayList comments = extractRawComments(content);
213:
214: // find the first properly-formatted comment
215: String firstComment = null;
216: final int n = comments.size();
217: for (int i = 0; i < n; i++) {
218: String this Comment = (String) comments.get(i);
219:
220: // see if it matches the special js2doc format
221: if (this Comment.charAt(0) == '*') {
222: // it does -- extract the contents and save them.
223: String js2docComment = this Comment.substring(1);
224: firstComment = js2docComment;
225: break;
226: }
227: }
228:
229: return extractJS2DocComment(firstComment);
230: }
231:
232: String textContent;
233: Set keywords;
234: List fields;
235:
236: public class FieldMap {
237: String key;
238: String value;
239:
240: public FieldMap(String key, String value) {
241: super ();
242: this .key = key;
243: this .value = value;
244: }
245:
246: public String getKey() {
247: return this .key;
248: }
249:
250: public String getValue() {
251: return this .value;
252: }
253: };
254:
255: public Comment() {
256: super ();
257: this .textContent = "";
258: this .keywords = new HashSet();
259: this .fields = new ArrayList();
260: }
261:
262: public boolean isEmpty() {
263: return (textContent.length() == 0) && keywords.isEmpty()
264: && fields.isEmpty();
265: }
266:
267: public void addField(String name, String value) {
268: fields.add(new FieldMap(name, value));
269: }
270:
271: public boolean hasField(String name) {
272: boolean found = false;
273: Iterator i = fields.iterator();
274: while (i.hasNext()) {
275: FieldMap fieldEntry = (FieldMap) i.next();
276: if (fieldEntry.getKey().compareTo(name) == 0) {
277: found = true;
278: break;
279: }
280: }
281: return found;
282: }
283:
284: public org.w3c.dom.Element appendAsXML(org.w3c.dom.Node parentNode) {
285: org.w3c.dom.Document ownerDocument = parentNode
286: .getOwnerDocument();
287:
288: org.w3c.dom.Element doc = (org.w3c.dom.Element) JS2DocUtils
289: .firstChildNodeWithName(parentNode, "doc");
290: if (doc == null) {
291: doc = parentNode.getOwnerDocument().createElement("doc");
292: parentNode.appendChild(doc);
293: }
294:
295: if (this .textContent.length() > 0) {
296: org.w3c.dom.Element textNode = (org.w3c.dom.Element) JS2DocUtils
297: .firstChildNodeWithName(doc, "text");
298: if (textNode == null) {
299: textNode = ownerDocument.createElement("text");
300: doc.appendChild(textNode);
301: }
302: JS2DocUtils.setXMLContent(textNode, this .textContent);
303: }
304:
305: Iterator fieldIter = this .fields.iterator();
306: while (fieldIter.hasNext()) {
307: Comment.FieldMap entry = (Comment.FieldMap) fieldIter
308: .next();
309:
310: String fieldName = entry.getKey(), fieldValue = entry
311: .getValue();
312:
313: org.w3c.dom.Element fieldNode = ownerDocument
314: .createElement("tag");
315: doc.appendChild(fieldNode);
316: fieldNode.setAttribute("name", fieldName.trim());
317: org.w3c.dom.Element textNode = ownerDocument
318: .createElement("text");
319: fieldNode.appendChild(textNode);
320: JS2DocUtils.setXMLContent(textNode, fieldValue);
321: }
322:
323: String keywordsToAdd = "";
324: Iterator keywordIter = this .keywords.iterator();
325: while (keywordIter.hasNext()) {
326: String keyword = (String) keywordIter.next();
327:
328: if (keywordsToAdd.length() == 0) {
329: keywordsToAdd = keyword;
330: } else {
331: keywordsToAdd += " " + keyword;
332: }
333: }
334: if (keywordsToAdd.length() != 0) {
335: doc.setAttribute("keywords", keywordsToAdd);
336: }
337:
338: parentNode.appendChild(doc);
339: return doc;
340: }
341:
342: };
|