/* gnu.classpath.tools.gjdoc.ExecutableMemberDocImpl
   Copyright (C) 2001 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */

package gnu.classpath.tools.gjdoc;

import java.util.*;
import java.io.*;
import com.sun.javadoc.*;

public class ExecutableMemberDocImpl extends MemberDocImpl implements ExecutableMemberDoc {

   protected ExecutableMemberDocImpl(ClassDoc containingClass,
				     PackageDoc containingPackage,
                                     SourcePosition position) {
      
      super(containingClass,
	    containingPackage,
            position);
   }

   protected boolean processModifier(String word) {
      if (super.processModifier(word)) {
	 return true;
      }
      else if (word.equals("synchronized")) {
	 isSynchronized=true;
	 return true;	 
      }
      else if (word.equals("native")) {
	 isNative=true;
	 return true;	 
      }
      else if (word.equals("abstract")) {
	 isAbstract=true;
	 return true;	 
      }
      else {
	 return false;
      }
   }

   private boolean isAbstract=false;
   private boolean isNative=false;
   private boolean isSynchronized=false;

   public boolean isAbstract() { return isAbstract; }

   public boolean isNative() { return isNative; }

   public boolean isSynchronized() { return isSynchronized; }

   public ClassDoc[] thrownExceptions() { return thrownExceptions; }

   public Parameter[] parameters() { return parameters; }

   public ThrowsTag[] throwsTags() { 
      return (ThrowsTag[])getTagArr("throws", throwsTagEmptyArr);
   }

   public ParamTag[] paramTags() { 
      return (ParamTag[])getTagArr("param", paramTagEmptyArr);
   }

   public String signature() { return signature; }
   public String flatSignature() { return flatSignature; }

   public ClassDoc overriddenClass() { 
      for (ClassDoc cdi=(ClassDoc)containingClass().superclass(); cdi!=null; cdi=(ClassDoc)cdi.superclass()) {
	 if (null!=ClassDocImpl.findMethod(cdi, name(), signature()))
	    return cdi;
      }
      return null;
   }

   public static ExecutableMemberDocImpl createFromSource(ClassDoc containingClass,
							  PackageDoc containingPackage,
							  char[] source, int startIndex, int endIndex) throws IOException, ParseException {

      int lastchar=32;
      StringBuffer methodName=new StringBuffer();
      for (int i=startIndex; i<endIndex && source[i]!='('; ++i) {
	 if ((Parser.WHITESPACE.indexOf(lastchar)>=0 && Parser.WHITESPACE.indexOf(source[i])<0)
             || (lastchar == ']' && Parser.WHITESPACE.indexOf(source[i])<0 && '[' != source[i])) {
            methodName.setLength(0);
            methodName.append(source[i]);
         }
	 else if (Parser.WHITESPACE.indexOf(source[i])<0) {
            methodName.append(source[i]);
         }
	 lastchar=source[i];
      }

      ExecutableMemberDocImpl rc;

      SourcePosition position = DocImpl.getPosition(containingClass, source, startIndex);

      if (methodName.toString().equals(((ClassDocImpl)containingClass).getClassName())) {
	 
	 // Constructor

	 rc=new ConstructorDocImpl(containingClass,
				   containingPackage,
                                   position);
      }
      else {

	 // Normal method

	 rc=new MethodDocImpl(containingClass,
			      containingPackage,
                              position);
      }
      
      if (containingClass.isInterface())
	 rc.accessLevel=ACCESS_PUBLIC;

      int ndx=rc.parseModifiers(source, startIndex, endIndex);
      StringBuffer name = new StringBuffer();

      final int STATE_NORMAL=1;
      final int STATE_STARC=2;
      final int STATE_SLASHC=3;

      int state=STATE_NORMAL;

      while (source[ndx]!='(' && ndx<endIndex) {
	 if (state==STATE_NORMAL) {
	    if (ndx<endIndex-1 && source[ndx]=='/' && source[ndx+1]=='/') {
	       ++ndx;
	       state=STATE_SLASHC;
	    }
	    else if (ndx<endIndex-1 && source[ndx]=='/' && source[ndx+1]=='*') {
	       ++ndx;
	       state=STATE_STARC;
	    }
	    else {
	       name.append(source[ndx]);
	    }
	 }
	 else if (state==STATE_SLASHC) {
	    if (source[ndx]=='\n')
	       state=STATE_NORMAL;
	 }
	 else if (state==STATE_STARC) {
	    if (ndx<endIndex-1 && source[ndx]=='*' && source[ndx+1]=='/') {
	       ++ndx;
	       state=STATE_NORMAL;
	    }
	 }
	 ++ndx;
      }
      rc.setName(name.toString().trim());

      state=STATE_NORMAL;
      
      ++ndx;
      int endx;
      String param="";
      List parameterList=new ArrayList();
      for (endx=ndx; endx<endIndex; ++endx) {
	 if (state==STATE_SLASHC) {
	    if (source[endx]=='\n') {
	       state=STATE_NORMAL;
	    }
	 }
	 else if (state==STATE_STARC) {
	    if (source[endx]=='*' && source[endx+1]=='/') {
	       state=STATE_NORMAL;
	       ++endx;
	    }
	 }
	 else if (source[endx]=='/' && source[endx+1]=='*') {
	    state=STATE_STARC;
	    ++endx;
	 }
	 else if (source[endx]=='/' && source[endx+1]=='/') {
	    state=STATE_SLASHC;
	    ++endx;
	 }
	 else if (source[endx]==',' || source[endx]==')') {
	    param=param.trim();
	    if (param.length()>0) {
	       int n = param.length()-1;
               int paramNameStart = 0;
               while (n >= 0) {
                  char c = param.charAt(n);
		  if ('[' == c || ']' == c || Parser.WHITESPACE.indexOf(c)>=0) {
                     paramNameStart = n + 1;
		     break;
                  }
                  else {
                     -- n;
                  }
               }
               while (n >= 0 && ('[' == param.charAt(n)
                                 || ']' == param.charAt(n)
                                 || Parser.WHITESPACE.indexOf(param.charAt(n))>=0)) {
                  -- n;
               }
               int paramTypeEnd = n + 1;
               int paramTypeStart = 0;
               while (n >= 0) {
                  char c = param.charAt(n);
		  if ('[' == c || ']' == c || Parser.WHITESPACE.indexOf(c)>=0) {
                     paramTypeStart = n + 1;
		     break;
                  }
                  else {
                     -- n;
                  }
               }

	       String paramType;
	       String paramName;
	       if (0 != paramNameStart) {
		  paramType=param.substring(paramTypeStart, paramTypeEnd);
		  paramName=param.substring(paramNameStart);
	       }
               else {
                  paramName = "";
                  StringBuffer paramTypeBuffer = new StringBuffer();
                  for (int i=0; i<param.length(); ++i) {
                     char c = param.charAt(i);
                     if ('[' != c && ']' != c && Parser.WHITESPACE.indexOf(c)<0) {
                        paramTypeBuffer.append(c);
                     }
                  }
                  paramType = paramTypeBuffer.toString();
               }
	       String dimSuffix="";

               for (int i=0; i<param.length(); ++i) {
                  if ('[' == param.charAt(i)) {
                     dimSuffix += "[]";
                  }
               }
	       paramType+=dimSuffix;

               if (paramType.startsWith("[")) {
                  System.err.println("broken param type in " + rc + " in " +containingClass);
               }

	       parameterList.add(new ParameterImpl(paramName, paramType, 
						   ((ClassDocImpl)containingClass).typeForString(paramType)));

	       param="";
	    }
	 }
	 else
	    param+=source[endx];

	 if (source[endx]==')' && state==STATE_NORMAL)
	    break;
      }

      rc.setParameters((Parameter[])parameterList.toArray(new Parameter[0]));
      
      ++endx;
      String word="";
      String dimSuffix="";
      boolean haveThrowsKeyword=false;
      List thrownExceptionsList=new ArrayList();

      state=STATE_NORMAL;
      for (; endx<endIndex; ++endx) {
	 if (state==STATE_SLASHC) {
	    if (source[endx]=='\n') state=STATE_NORMAL;
	 }
	 else if (state==STATE_STARC) {
	    if (source[endx]=='*' && source[endx+1]=='/') {
	       state=STATE_NORMAL;
	       ++endx;
	    }
	 }
	 else if (source[endx]=='/' && source[endx+1]=='*') {
	    state=STATE_STARC;
	    ++endx;
	 }
	 else if (source[endx]=='/' && source[endx+1]=='/') {
	    state=STATE_SLASHC;
	    ++endx;
	 }
	 else if (Parser.WHITESPACE.indexOf(source[endx])>=0) {
	    word=word.trim();
	    if (!haveThrowsKeyword && word.length()>0) {
	       if (word.equals("throws")) haveThrowsKeyword=true;
	       else System.err.println("ARGH! "+word);
	       word="";
	    }
	 }
	 else if (source[endx]=='[' || source[endx]==']') {
	    dimSuffix += source[endx];
	 }
	 else if (source[endx]==',' || source[endx]=='{' || source[endx]==';') {
	    word=word.trim();
	    if (word.length()>0) {
	       ClassDoc exceptionType=rc.containingClass().findClass(word);
	       if (exceptionType==null) {
		  exceptionType=new ClassDocProxy(word, 
                                                  rc.containingClass());
	       }
	       thrownExceptionsList.add(exceptionType);
	    }
	    if (source[endx]=='{') {
	       break;
	    }
	    else {
	       word="";
	    }
	 }
	 else {
	    word+=source[endx];
	 }
      }

      if (dimSuffix.length()>0) {
	 rc.setTypeName(rc.getTypeName()+dimSuffix);
      }

      rc.setThrownExceptions((ClassDoc[])thrownExceptionsList.toArray(new ClassDoc[0]));

      return rc;
   }

   private ClassDoc[] thrownExceptions;
   private Parameter[] parameters;
   private String signature;
   private String flatSignature;

   void setParameters(Parameter[] parameters) {
      this.parameters=parameters;
   }

   void setThrownExceptions(ClassDoc[] thrownExceptions) {
      this.thrownExceptions=thrownExceptions;
   }

   void resolve() {

      for (int i=0; i<thrownExceptions.length; ++i) {
	 if (thrownExceptions[i] instanceof ClassDocProxy) {
	    String className=thrownExceptions[i].qualifiedName();
	    ClassDoc realClassDoc=containingClass().findClass(className);
	    if (realClassDoc!=null)
	       thrownExceptions[i]=realClassDoc;
	 }
      }

      StringBuffer signatureBuf=new StringBuffer();
      StringBuffer flatSignatureBuf=new StringBuffer();

      for (int i=0; i<parameters.length; ++i) {
	 ((ParameterImpl)parameters[i]).resolve(containingClass());

	 if (signatureBuf.length()>0) {
	    signatureBuf.append(",");
	    flatSignatureBuf.append(",");
	 }
	 signatureBuf.append(parameters[i].type().qualifiedTypeName());
	 flatSignatureBuf.append(parameters[i].type().typeName());
         signatureBuf.append(parameters[i].type().dimension());
         flatSignatureBuf.append(parameters[i].type().dimension());
      }
      this.signature="("+signatureBuf.toString()+")";
      this.flatSignature="("+flatSignatureBuf.toString()+")";

      super.resolve();

   }

   public int compareTo(Object other) {
      int rc;
      if (other instanceof MemberDocImpl) {
         MemberDocImpl otherMember = (MemberDocImpl)other;
	 rc = name().compareTo(otherMember.name());
         if (0 == rc) {
            if (other instanceof ExecutableMemberDocImpl) {
               rc = signature().compareTo(((ExecutableMemberDocImpl)other).signature());
               if (0 == rc) {
                  return containingClass().compareTo(otherMember.containingClass());
               }
            }
            else {
               rc = 1;
            }
         }
      }
      else {
	 rc = 1;
      }
      return rc;
   }
}

