001: /*=============================================================================
002: * Copyright Texas Instruments 2000-2004. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.data;
022:
023: import java.io.*;
024: import java.util.*;
025:
026: import oscript.syntaxtree.FunctionPrimaryPrefix;
027: import oscript.util.StackFrame;
028: import oscript.util.MemberTable;
029: import oscript.util.SymbolTable;
030: import oscript.exceptions.*;
031: import oscript.NodeEvaluator;
032:
033: /**
034: * A script function/constructor. Since native (and other) objects that
035: * behave as a function can re-use some functionality (ie checking number
036: * of args, type casting, etc., the stuff specific to a script function/
037: * constructor is pushed out into a seperate class.
038: *
039: * @author Rob Clark (rob@ti.com)
040: */
041: public class Function extends Type {
042: /**
043: * The type object for an script function.
044: */
045: public final static Value TYPE = BuiltinType
046: .makeBuiltinType("oscript.data.Function");
047: public final static String PARENT_TYPE_NAME = "oscript.data.OObject";
048: public final static String TYPE_NAME = "Function";
049: public final static String[] MEMBER_NAMES = new String[] {
050: "getName", "getComment", "getMinimumArgCount",
051: "takesVarArgs", "getArgNames", "isA", "castToString",
052: "callAsFunction", "callAsConstructor", "callAsExtends" };
053:
054: /**
055: * The scope this function is defined in. This does not change throughout
056: * the life of the function.
057: */
058: private final Scope enclosingScope;
059:
060: /**
061: * The scope the static members of this function are defined in. When
062: * the function is constructed, if there are any static members, they
063: * are evaluated within this scope. Otherwise this is null.
064: */
065: private final Scope staticScope;
066:
067: /**
068: * The function this function extends, if any.
069: */
070: private final Value super Fxn;
071:
072: /**
073: * The shared function data... parameters that are shared by all instances
074: * of the same function.
075: * <p>
076: * public for {@link StackFrame#evalNode}
077: */
078: public final FunctionData fd;
079:
080: /**
081: * If this function overrides a value, this is the previous value.
082: */
083: private Value overriden;
084:
085: /**
086: * In order to keep function instances more lightweight, the values that
087: * will be the same for any instance of a function representing the same
088: * portion of the parse tree have been split out into this class, in order
089: * to be shared between different function instances.
090: */
091: public static final class FunctionData implements Externalizable {
092: /*
093: * XXX all these would be final if it weren't for needing the no-arg
094: * constructor for serialization
095: */
096:
097: /**
098: * The node-evaluator for evaluating the body of this function.
099: * <p>
100: * only public for {@link StackFrame}
101: */
102: public NodeEvaluator program;
103:
104: /**
105: * The node-evaluator for evaluating the static body of this function.
106: */
107: NodeEvaluator sprogram;
108:
109: /**
110: * The expressions to evaluate to determine args to superFxn.
111: */
112: NodeEvaluator exprList;
113:
114: /**
115: * The id of this function.
116: */
117: int id;
118:
119: /**
120: * The ids of the arguments and there permissions. The n'th parameter
121: * to the function has its <code>id</code> specified by the 2*n element
122: * in the array, and <code>attr</code> specified by the 2*n+1 element in
123: * the array.
124: */
125: int[] argIds;
126:
127: /**
128: * The number of args (if not vararg fxn), or minimum number of args (if
129: * vararg fxn).
130: */
131: int nargs;
132:
133: /**
134: * Is this a var-args function. If it is, then the zero or more remaining
135: * args to the function are copied into an array that is assigned to the
136: * last arg to the function.
137: */
138: boolean varargs;
139:
140: /**
141: * A hint from the parser about whether we need to create an extra level
142: * of scope (ie. there are variables declared in that scope)
143: */
144: boolean hasVarInScope;
145:
146: /**
147: * A hint from the parser about whether scope storage can be allocated from
148: * the stack, which can only be done if there are no functions declared
149: * within this function
150: * <p>
151: * only public for {@link StackFrame}
152: */
153: public boolean hasFxnInScope;
154:
155: /**
156: * Comment generated from javadoc comment block in src file.
157: */
158: Value comment;
159:
160: /**
161: * Class Constructor.
162: *
163: * @param id the id of the symbol that maps to the member, ie. it's name
164: * @param argIds array of argument ids and attributes
165: * @param varargs is this a function that can take a variable number of args?
166: * @param exprList expressions to evaluate to get args to <code>superFxn</code>
167: * or <code>null</code> if <code>superFxn</code> is <code>null</code>
168: * @param program the body of the function
169: * @param sprogram the static body of the function, or <code>null</code>
170: * @param hasVarInScope whether one or more vars/functions are declared in the
171: * function body's scope... this is a hint from the parser to tell us if we
172: * can avoid creating a scope object at runtime
173: * @param hasFxnInScope whether one or more functions are enclosed by this function
174: * body's scope... this is a hint from the parser to tell us if we can allocate
175: * scope storage from the stack
176: * @param comment html formatted comment generated from javadoc
177: * comment in src file, or <code>null</code>
178: */
179: public FunctionData(int id, int[] argIds, boolean varargs,
180: NodeEvaluator exprList, NodeEvaluator program,
181: NodeEvaluator sprogram, boolean hasVarInScope,
182: boolean hasFxnInScope, Value comment) {
183: this .id = id;
184: this .argIds = argIds;
185: this .varargs = varargs;
186: this .exprList = exprList;
187: this .program = program;
188: this .sprogram = sprogram;
189: this .hasVarInScope = hasVarInScope;
190: this .hasFxnInScope = hasFxnInScope;
191: this .comment = comment;
192:
193: if (varargs)
194: nargs = (argIds.length / 2) - 1;
195: else
196: nargs = (argIds.length / 2);
197: }
198:
199: /*
200: * Externalizable Support:
201: */
202:
203: public FunctionData() {
204: }
205:
206: /**
207: * Derived class that implements {@link java.io.Externalizable} must
208: * call this if it overrides it. It should override it to save/restore
209: * it's own state.
210: */
211: public void readExternal(ObjectInput in) throws IOException,
212: ClassNotFoundException {
213: program = (NodeEvaluator) (in.readObject());
214: sprogram = (NodeEvaluator) (in.readObject());
215: exprList = (NodeEvaluator) (in.readObject());
216: id = in.readInt();
217: argIds = new int[in.readInt()];
218: for (int i = 0; i < argIds.length; i++)
219: argIds[i] = in.readInt();
220: nargs = in.readInt();
221: varargs = in.readBoolean();
222: hasVarInScope = in.readBoolean();
223: hasFxnInScope = in.readBoolean();
224: if (in.readByte() == 1) {
225: comment = new OString();
226: comment.readExternal(in);
227: }
228: }
229:
230: /**
231: * Derived class that implements {@link Externalizable} must
232: * call this if it overrides it. It should override it to save/restore
233: * it's own state.
234: */
235: public void writeExternal(ObjectOutput out) throws IOException {
236: out.writeObject(program);
237: out.writeObject(sprogram);
238: out.writeObject(exprList);
239: out.writeInt(id);
240: out.writeInt(argIds.length);
241: for (int i = 0; i < argIds.length; i++)
242: out.writeInt(argIds[i]);
243: out.writeInt(nargs);
244: out.writeBoolean(varargs);
245: out.writeBoolean(hasVarInScope);
246: out.writeBoolean(hasFxnInScope);
247: if (comment != null) {
248: out.writeByte(1);
249: comment.writeExternal(out);
250: } else {
251: out.writeByte(0);
252: }
253: }
254:
255: /**
256: * Map arguments to a function into the member-table which is used for
257: * a function scope. Since the compiler ensures that the function
258: * parameters map to idx 0 thru n in the function-scope, all this has
259: * to do is collapse the var-arg parameter (if present) into a single
260: * array, and if there is a function within this function's body copy
261: * into new table.. This also ensures that the correct number of parameters is
262: * passed to the function. This is used instead of {@link #addArgs}
263: * when calling as a function.
264: * <p>
265: * XXX this could be used in case of constructor scope, by stripping
266: * out private parameters... maybe
267: *
268: * @param args the input arguments
269: * @return
270: */
271: public final MemberTable mapArgs(MemberTable args) {
272: if (hasFxnInScope)
273: args = args.safeCopy();
274:
275: int alen = args.length();
276:
277: if ((alen == nargs) || (varargs && (alen >= nargs))) {
278: if (varargs) {
279: // XXX in theory, it should be possible to bring back an optimization
280: // to avoid the copy, if nargs==0....
281:
282: OArray arr = new OArray(alen - nargs);
283: for (int i = nargs; i < alen; i++)
284: arr.elementAt(i - nargs).opAssign(
285: args.referenceAt(i));
286:
287: args.ensureCapacity(nargs);
288: args.referenceAt(nargs).reset(arr);
289: }
290:
291: return args;
292: } else {
293: throw PackagedScriptObjectException
294: .makeExceptionWrapper(new OIllegalArgumentException(
295: "wrong number of args!"));
296: }
297: }
298:
299: /**
300: * A helper to populate a fxn-scope with args
301: */
302: public final void addArgs(FunctionScope fxnScope,
303: MemberTable args) {
304: int len = (args == null) ? 0 : args.length();
305:
306: if ((len == nargs) || (varargs && (len >= nargs))) {
307: for (int i = 0; i < nargs; i++) {
308: int id = argIds[2 * i];
309: int attr = argIds[2 * i + 1];
310:
311: fxnScope.createMember(id, attr).opAssign(
312: args.referenceAt(i));
313: }
314:
315: if (varargs) {
316: int id = argIds[2 * nargs];
317: int attr = argIds[2 * nargs + 1];
318:
319: // XXX in theory, it should be possible to bring back an optimization
320: // to avoid the copy, if nargs==0....
321:
322: OArray arr = new OArray(len - nargs);
323: for (int i = nargs; i < len; i++)
324: arr.elementAt(i - nargs).opAssign(
325: args.referenceAt(i));
326:
327: fxnScope.createMember(id, attr).opAssign(arr);
328: }
329: } else {
330: throw PackagedScriptObjectException
331: .makeExceptionWrapper(new OIllegalArgumentException(
332: "wrong number of args!"));
333: }
334: }
335:
336: public Value getName() {
337: return Symbol.getSymbol(id);
338: }
339: }
340:
341: /*=======================================================================*/
342: /**
343: * Class Constructor. Construct an anonymous function.
344: *
345: * @param enclosingScope the context the function was declared in
346: * @param superFxn the function this function extends, or
347: * <code>null</code>
348: * @param fd the shared function data, for all instances
349: * of this function
350: *
351: */
352: public Function(Scope enclosingScope, Value super Fxn,
353: FunctionData fd) {
354: super ();
355:
356: // every script type implicitly inherits from <i>Object</i>
357: if (super Fxn == null)
358: super Fxn = OObject.TYPE;
359:
360: // if this function is overriding a function in an object scope, keep a
361: // reference to the overriden function:
362: if ((fd.id != FunctionPrimaryPrefix.ANON_FXN_ID)
363: && (enclosingScope instanceof ConstructorScope)) {
364: Scope scope = enclosingScope.getPreviousScope();
365: while (!(scope instanceof ScriptObject))
366: scope = scope.getPreviousScope();
367: overriden = scope.getMemberImpl(fd.id);
368: if (overriden != null)
369: overriden = overriden.unhand();
370: }
371:
372: // XXX seems to cause apple's VM to bus-error... not sure if it causes
373: // problems on other platforms or not. --RDC
374: // if(DEBUG)
375: // if( !enclosingScope.isSafe() )
376: // StackFrame.dumpStack(System.err);
377:
378: this .enclosingScope = enclosingScope;
379: this .super Fxn = super Fxn;
380: this .fd = fd;
381:
382: if (fd.sprogram != null) {
383: staticScope = new BasicScope(enclosingScope);
384: StackFrame.currentStackFrame().evalNode(fd.sprogram,
385: staticScope);
386: } else {
387: staticScope = null;
388: }
389: }
390:
391: /*=======================================================================*/
392: /**
393: * Get the function that this function extends, or <code>null</code> if
394: * none.
395: */
396: Value getSuper() {
397: return super Fxn;
398: }
399:
400: /*=======================================================================*/
401: /**
402: * If this function overrides a value, this method returns it. Otherwise
403: * it returns <code>null</code>.
404: */
405: Value getOverriden() {
406: return overriden;
407: }
408:
409: /*=======================================================================*/
410: /**
411: * Get the type of this object. The returned type doesn't have to take
412: * into account the possibility of a script type extending a built-in
413: * type, since that is handled by {@link #getType}.
414: *
415: * @return the object's type
416: */
417: protected Value getTypeImpl() {
418: return TYPE;
419: }
420:
421: /*=======================================================================*/
422: /**
423: * Get the name of this function. An anonymous function will have the
424: * name "anon".
425: *
426: * @return the function's name
427: */
428: public Value getName() {
429: return fd.getName();
430: }
431:
432: /*=======================================================================*/
433: /**
434: * Get the comment block. If there was a javadoc comment block preceding
435: * the definition of this function in the src file, it can be accessed
436: * with this method.
437: *
438: * @return the function's comment, or <code>null</code>
439: */
440: public Value getComment() {
441: return fd.comment;
442: }
443:
444: /*=======================================================================*/
445: /**
446: * Get the minimum number of args that should be passed to this function.
447: * If {@link #isVarArgs} returns <code>true</code>, then it is possible
448: * to pass more arguments to this function, otherwise, you should pass
449: * exactly this number of args to the function.
450: *
451: * @return minimum number of args to pass when calling this function
452: * @see #isVarArgs
453: * @see #getArgNames
454: */
455: public int getMinimumArgCount() {
456: return fd.nargs;
457: }
458:
459: /*=======================================================================*/
460: /**
461: * Can this function be called with a variable number of arguments?
462: * @see #getMinimumArgCount
463: * @see #getArgNames
464: */
465: public boolean takesVarArgs() {
466: return fd.varargs;
467: }
468:
469: /*=======================================================================*/
470: /**
471: * Get the names of the arguments to the function, in order. If this
472: * function takes a variable number of arguments, the last name in the
473: * array is the "var-arg" variable, to which the array of all remaining
474: * arguments are bound.
475: */
476: public Value[] getArgNames() {
477: Value[] names = new Value[fd.argIds.length / 2];
478: for (int i = 0; i < names.length; i++)
479: names[i] = Symbol.getSymbol(fd.argIds[2 * i]);
480: return names;
481: }
482:
483: /* Note: arg-permissions are not made visible, because that seems
484: * to me like an implementation detail, whereas the arg-names
485: * is (sort of) part of the interface
486: */
487:
488: /*=======================================================================*/
489: /**
490: * If this object is a type, determine if an instance of this type is
491: * an instance of the specified type, ie. if this is <code>type</code>,
492: * or a subclass.
493: *
494: * @param type the type to compare this type to
495: * @return <code>true</code> or <code>false</code>
496: * @throws PackagedScriptObjectException(NoSuchMemberException)
497: */
498: public boolean isA(Value type) {
499: return super .isA(type) || this .super Fxn.isA(type);
500: }
501:
502: private static final int BOPCAST = Symbol.getSymbol("_bopCast")
503: .getId();
504:
505: /*=======================================================================*/
506: /**
507: * Perform the cast operation, <code>(a)b</code> is equivalent to <code>a.bopCast(b)</code>
508: *
509: * @param val the other value
510: * @return the result
511: * @throws PackagedScriptObjectException(NoSuchMemberException)
512: */
513: public Value bopCast(Value val)
514: throws PackagedScriptObjectException {
515: Value bopCast = getMember(BOPCAST, false);
516: if (bopCast != null)
517: return bopCast.callAsFunction(new Value[] { val });
518: return super .bopCast(val);
519: }
520:
521: // bopCastR would be an instance member, not static (class) member
522:
523: /*=======================================================================*/
524: /**
525: * Convert this object to a native java <code>String</code> value.
526: *
527: * @return a String value
528: * @throws PackagedScriptObjectException(NoSuchMethodException)
529: */
530: public String castToString() throws PackagedScriptObjectException {
531: return "[function: " + getName() + "]";
532: }
533:
534: /*=======================================================================*/
535: /**
536: * Call this object as a function.
537: *
538: * @param sf the current stack frame
539: * @param args the arguments to the function, or <code>null</code> if none
540: * @return the value returned by the function
541: * @throws PackagedScriptObjectException
542: * @see Function
543: */
544: public Value callAsFunction(StackFrame sf, MemberTable args)
545: throws PackagedScriptObjectException {
546: if (super Fxn != OObject.TYPE)
547: throw PackagedScriptObjectException
548: .makeExceptionWrapper(new OUnsupportedOperationException(
549: getName() + ": cannot call as function!"));
550:
551: if (!fd.hasVarInScope && (fd.argIds.length == 0)
552: && (args == null)) {
553: return (Value) (sf.evalNode(fd.program, enclosingScope));
554: } else {
555: Scope scope;
556: SymbolTable smit = fd.program
557: .getSharedMemberIndexTable(NodeEvaluator.ALL);
558: if (args == null)
559: args = fd.hasFxnInScope ? new OArray(0) : sf
560: .allocateMemberTable((short) 0);
561: args = fd.mapArgs(args); // even if args length is zero, to deal with var-args
562: if (!fd.hasFxnInScope)
563: scope = sf.allocateFunctionScope(this , enclosingScope,
564: smit, args);
565: else
566: scope = new FunctionScope(this , enclosingScope, smit,
567: args);
568: try {
569: return (Value) (sf.evalNode(fd.program, scope));
570: } finally {
571: scope.free();
572: }
573: }
574: }
575:
576: /*=======================================================================*/
577: /**
578: * Call this object as a constructor.
579: *
580: * @param sf the current stack frame
581: * @param args the arguments to the function, or <code>null</code> if none
582: * @return the newly constructed object
583: * @throws PackagedScriptObjectException
584: * @see Function
585: */
586: public Value callAsConstructor(StackFrame sf, MemberTable args)
587: throws PackagedScriptObjectException {
588: /* XXX we should only need to create ConstructorScope if the number of
589: * args is greater than zero, and hasVarInScope
590: */
591: ScriptObject newThisScope = new ScriptObject(
592: this ,
593: enclosingScope,
594: fd.program
595: .getSharedMemberIndexTable(NodeEvaluator.PUBPROT));
596: ConstructorScope fxnScope = new ConstructorScope(
597: this ,
598: newThisScope,
599: fd.program
600: .getSharedMemberIndexTable(NodeEvaluator.PRIVATE));
601:
602: fd.addArgs(fxnScope, args);
603:
604: MemberTable super FxnArgs;
605: if (fd.exprList != null)
606: super FxnArgs = (MemberTable) (sf.evalNode(fd.exprList,
607: fxnScope));
608: else
609: super FxnArgs = new OArray(0);
610:
611: super Fxn.callAsExtends(sf, newThisScope, super FxnArgs);
612:
613: sf.evalNode(fd.program, fxnScope);
614:
615: return newThisScope;
616: }
617:
618: /*=======================================================================*/
619: /**
620: * Call this object as a parent class constructor.
621: *
622: * @param sf the current stack frame
623: * @param scope the object
624: * @param args the arguments to the function, or <code>null</code> if none
625: * @return the value returned by the function
626: * @throws PackagedScriptObjectException
627: * @see Function
628: */
629: public Value callAsExtends(StackFrame sf, Scope scope,
630: MemberTable args) throws PackagedScriptObjectException {
631: /* XXX we should only need to create ConstructorScope if the number of
632: * args is greater than zero, and hasVarInScope
633: */
634:
635: scope = new ForkScope(scope, enclosingScope);
636: ConstructorScope fxnScope = new ConstructorScope(
637: this ,
638: scope,
639: fd.program
640: .getSharedMemberIndexTable(NodeEvaluator.PRIVATE));
641:
642: fd.addArgs(fxnScope, args);
643:
644: MemberTable super FxnArgs;
645: if (fd.exprList != null)
646: super FxnArgs = (MemberTable) (sf.evalNode(fd.exprList,
647: fxnScope));
648: else
649: super FxnArgs = new OArray(0);
650:
651: super Fxn.callAsExtends(sf, scope, super FxnArgs);
652:
653: return (Value) (sf.evalNode(fd.program, fxnScope));
654: }
655:
656: /*=======================================================================*/
657: /**
658: * Get a member of this object.
659: *
660: * @param id the id of the symbol that maps to the member
661: * @param exception whether an exception should be thrown if the
662: * member object is not resolved
663: * @return a reference to the member
664: * @throws PackagedScriptObjectException(NoSuchMethodException)
665: * @throws PackagedScriptObjectException(NoSuchMemberException)
666: */
667: public Value getMember(int id, boolean exception)
668: throws PackagedScriptObjectException {
669: Value val = getStaticMember(id);
670:
671: if (val == null)
672: val = super .getMember(id, exception);
673:
674: return val;
675: }
676:
677: /*=======================================================================*/
678: /**
679: * Get a member of this type. This is used to interface to the java
680: * method of having members be attributes of a type. Regular object-
681: * script object's members are attributes of the object, but in the
682: * case of java types (including built-in types), the members are
683: * attributes of the type.
684: *
685: * @param obj an object of this type
686: * @param id the id of the symbol that maps to the member
687: * @return a reference to the member, or null
688: */
689: protected Value getTypeMember(Value obj, int id) {
690: Value val = super Fxn.getTypeMember(obj, id);
691:
692: if (val == null)
693: val = getStaticMember(id);
694:
695: return val;
696: }
697:
698: /*=======================================================================*/
699: /**
700: * Get a static member of this function object.
701: */
702: final Value getStaticMember(int id) {
703: Value val = null;
704:
705: if (staticScope != null)
706: val = staticScope.getMember(id, false);
707:
708: return val;
709: }
710:
711: /*=======================================================================*/
712: /**
713: * Derived classes that implement {@link #getMember} should also
714: * implement this.
715: *
716: * @param s the set to populate
717: * @param debugger <code>true</code> if being used by debugger, in
718: * which case both public and private/protected field names should
719: * be returned
720: * @see #getMember
721: */
722: protected void populateMemberSet(Set s, boolean debugger) {
723: if (staticScope != null)
724: staticScope.populateMemberSet(s, debugger);
725: }
726:
727: /*=======================================================================*/
728: /**
729: * Derived classes that implement {@link #getTypeMember} should also
730: * implement this.
731: *
732: * @param s the set to populate
733: * @param debugger <code>true</code> if being used by debugger, in
734: * which case both public and private/protected field names should
735: * be returned
736: * @see #getTypeMember
737: */
738: protected void populateTypeMemberSet(Set s, boolean debugger) {
739: if (super Fxn != null)
740: super Fxn.populateTypeMemberSet(s, debugger);
741: }
742:
743: /*=======================================================================*/
744: /**
745: */
746: public static Value extractJavadocComment(Vector specials,
747: Value name, int[] argIds) {
748: Value[] argNames = new Value[argIds.length / 2];
749: for (int i = 0; i < argNames.length; i++)
750: argNames[i] = Symbol.getSymbol(argIds[2 * i]);
751:
752: StringBuffer sb = new StringBuffer();
753:
754: for (int i = 0; i < specials.size(); i++)
755: sb.append(((oscript.syntaxtree.NodeToken) (specials
756: .elementAt(i))).toString());
757:
758: return extractJavadocComment(sb.toString(), name, argNames);
759: }
760:
761: public static Value extractJavadocComment(String str, Value name,
762: Value[] argNames) {
763: int idx = str.lastIndexOf("/**"); /**/
764:
765: if (idx == -1)
766: return null;
767:
768: str = str.substring(idx + "/**".length()); /**/
769:
770: StringBuffer sb = new StringBuffer();
771:
772: sb.append("<html><head><title>" + name
773: + "</title></head><body>");
774: extractJavadocCommentBodyImpl(sb, str, name, argNames);
775: sb.append("</body></html>");
776:
777: return new OString(sb.toString());
778: }
779:
780: public static Value extractJavadocCommentBody(String str,
781: Value name, Value[] argNames) {
782: int idx = str.lastIndexOf("/**"); /**/
783:
784: if (idx == -1)
785: return null;
786:
787: str = str.substring(idx + "/**".length()); /**/
788:
789: StringBuffer sb = new StringBuffer();
790:
791: extractJavadocCommentBodyImpl(sb, str, name, argNames);
792:
793: return new OString(sb.toString());
794: }
795:
796: private static String extractJavadocCommentBodyImpl(
797: StringBuffer sb, String str, Value name, Value[] argNames) {
798: BufferedReader br = new BufferedReader(new StringReader(str));
799:
800: LinkedList paramList = new LinkedList();
801: LinkedList throwsList = new LinkedList();
802: LinkedList returnList = new LinkedList(); // shouldn't have more than one!
803:
804: sb.append("<pre>function <b>" + name + "</b>(");
805: for (int i = 0; i < argNames.length; i++)
806: sb.append(((i == 0) ? "" : ", ") + argNames[i]);
807: // if(fd.varargs)
808: // sb.append("...");
809: sb.append(")</pre>");
810:
811: try {
812: while ((str = strip(br.readLine())) != null) {
813: // check for @XXX parameters:
814: if (str.startsWith("@"))
815: break;
816: else
817: sb.append(str + " ");
818: }
819:
820: // handle @XXX paramters:
821: while (str != null) {
822: LinkedList list = null;
823: String match = null;
824:
825: if (str.startsWith("@param")) {
826: list = paramList;
827: match = "@param";
828: } else if (str.startsWith("@throws")) {
829: list = throwsList;
830: match = "@throws";
831: } else if (str.startsWith("@return")) {
832: list = returnList;
833: match = "@return";
834: }
835: // else ignore...
836:
837: if (list != null) {
838: str = str.substring(match.length()).trim();
839: String val = str;
840:
841: // check for continuation on following line:
842: do {
843: str = strip(br.readLine());
844: if ((str == null) || str.startsWith("@"))
845: break;
846: else
847: val += str;
848: } while (true);
849:
850: list.add(val);
851:
852: continue;
853: }
854:
855: str = strip(br.readLine());
856: }
857: } catch (IOException e) {
858: e.printStackTrace();
859: throw new ProgrammingErrorException(
860: "this shouldn't happen: " + e);
861: }
862:
863: if (paramList.size() > 0)
864: appendParamBlock(sb, "Parameters", paramList);
865:
866: if (throwsList.size() > 0)
867: appendParamBlock(sb, "Exceptions", throwsList);
868:
869: if (returnList.size() > 0)
870: sb.append("<dl><dt><b>Returns</b></dt><dd>"
871: + returnList.getFirst() + "</dd></dl>");
872:
873: return sb.toString();
874: }
875:
876: // strip off leading whitespace, and "*"'s
877: private static final String strip(String str) {
878: if (str == null)
879: return null;
880: str = str.trim();
881: while (str.startsWith("*/"))
882: str = str.substring(2);
883: while (str.startsWith("*"))
884: str = str.substring(1);
885: str = str.trim();
886: return str;
887: }
888:
889: private static final void appendParamBlock(StringBuffer sb,
890: String title, LinkedList list) {
891: sb.append("<dl><dt><b>" + title + "</b></dt>");
892:
893: for (Iterator itr = list.iterator(); itr.hasNext();) {
894: String str = (String) (itr.next());
895: String name;
896: int idx = str.indexOf(" ");
897:
898: if (idx != -1) {
899: name = str.substring(0, idx);
900: str = str.substring(idx).trim();
901: } else {
902: name = str;
903: str = "";
904: }
905:
906: sb.append("<dd><code>" + name + "</code> - " + str
907: + "</dd>");
908: }
909:
910: sb.append("</dl>");
911: }
912: }
913:
914: /*
915: * Local Variables:
916: * tab-width: 2
917: * indent-tabs-mode: nil
918: * mode: java
919: * c-indentation-style: java
920: * c-basic-offset: 2
921: * eval: (c-set-offset 'substatement-open '0)
922: * eval: (c-set-offset 'case-label '+)
923: * eval: (c-set-offset 'inclass '+)
924: * eval: (c-set-offset 'inline-open '0)
925: * End:
926: */
|