package parser.ServletParser.library;

import java.io.*;
import java.lang.*;
import java.util.*;
import parser.ServletParser.*;
import parser.ServletParser.javaparse.ParseTreeNode1;
import parser.ServletParser.javapass2.ParseTreeNode2;

/** 이 클래스는 파싱 이후에 특정 클래스에 대한 모든 정보를 관리한다.
 */

public
class ClassInfoNode extends Object implements Serializable
{
	/** 버전 제어를 위한 시리얼 넘버이다.
	 */
	public static final long serialVersionUID = 8618661577449251928L;
// Attributes
	/** 서블릿인가를 나타내는 flag이다.
	 */
	private boolean _servletFlag;
	/** 인터페이스 인가를 나타내는 flag이다.
	 */
	private boolean _interfaceFlag;
	/** 파일 이름을 저장하는 데이터 멤버이다. 
	 */
    private String _fileName;
	/** 패키지 이름을 저장하는 데이터 멤버이다. 
	 */
    private String _packageName;
 	/** 클래스 이름을 저장하는 데이터 멤버이다. 
	 */
	private String _className;
 	/** 슈퍼 클래스와 인터페이스에 대한 정보를 저장하는 데이터 멤버이다. 
	 *  처음에는 이름 만을 저장 했다가 클래스 정보 노드들이 모두 완성되면 
	 *  그 노드에 대한 레퍼런스로 바뀐다. 
	*/
    private ClassInfoNodeList _superClasses;
	/** 이 클래스에 소속된 데이타 멤버들에 대한 정보를 포함한다.
	 */
	private AttributeList _attributes;
	/** 이 클래스에 소속된 멤버 함수들에 대한 정보를 포함한다.
	 */
    private MethodList _methods;
	/** 이 클래스의 데이타 멤버와 슈퍼 클래스의 데이타 멤버들 모두에 대한 정보를 저장한다. 
	 */
	private VarRefBlock _classVarRefs;
	/** Lines of Code 메트릭 값
	 */
	private int _loc;
	/** Number of Import Statement 메트릭 값 
	 */
	private int _nois;
	/** Number of Added Methods 메트릭 값
	 */
	private int _noam;
	/** Number of Overridden Methods 메트릭 값
	 */
	private int _noom;
	/** Comments Ratio 메트릭 값
	 */
	private int _cr;
	/** True Comments Ratio 메트릭 값
	 */
	private int _tcr;
	/** 클래스에서 사용된 unique한 오퍼레이터들의 리스트이다.
	 */
	private StringList _uniqueOperators;
	/** 클래스에서 사용된 unique한 오퍼랜드들의 리스트이다.
	 */
	private StringList _uniqueOperands;
	/** 어트리뷰트 수이다.
	 */
	private int _numberOfAttributes;
	/** 프라이비트 혹은 프로텍티드 어트리뷰트 수이다.
	 */
	private int _numberOfInvisibleAttributes;
	/** 생성자를 제외한 메소드 수이다.
	 */
	private int _numberOfMethods;
	/** 프라이비트 혹은 프로텍티드 메소드 수이다.
	 */
	private int _numberOfInvisibleMethods;
	/** 상속된 어트리뷰트의 수이다.
	 */
	private int _nInheritedAttributes;
	/** 상속된 메소드의 수이다.
	 */
	private int _nInheritedMethods;
	/** Coupling Between Objects값이다.
	 */
	private int _cbo;
	/** 이 클래스와 상위 클래스에 속하는 함수이름들이다.
	 */
	private StringList _thisAndSuperMethods;
	/** 데이터멤버 이름들의 리스트이다.
	 *  LOCOM1을 구하는 과정에서 사용된다.
	 */
	public StringList dataMembers;
	/** 후손 노드의 수이다.
	 */
	private int _nDescendants;
	/** html file name 리스트이다.
	 */
	public StringList htmlList;
	/** PrinterWriter에 의해 출력된 HTML 파일 리스트이다.
	 */
	public StringList htmlOutputFullFileNameList;
	/** PrinterWriter에 의해 출력된 HTML 파일 리스트이다.
	 */
	public StringList htmlOutputFileList;
	/** 이 클래스에서 사용된 모든 sql 정보의 모음이다.
	 */
	public SQLInfoList sqlList;
	/** 생성자 이다.
	 */
    public ClassInfoNode(String className) {
		_servletFlag = false;
		_interfaceFlag = false;
		_packageName = new String("");
		_className = new String(className);
		_superClasses = new ClassInfoNodeList();
		_attributes = new AttributeList();
		_methods = new MethodList();
		_classVarRefs = new VarRefBlock();

		_loc = 0;
		_nois = 0;
		_noam = 0;
		_noom = 0;
		_cr = 0;
		_tcr = 0;
		_cbo = 0;
		_uniqueOperators = new StringList();
		_uniqueOperands = new StringList();

		_numberOfAttributes = 0;
		_numberOfInvisibleAttributes = 0;
		_numberOfMethods = 0;
		_numberOfInvisibleMethods = 0;
		_nInheritedAttributes = 0;
		_nInheritedMethods = 0;
		_thisAndSuperMethods = new StringList();
		dataMembers = new StringList();
		_nDescendants = 0;

		htmlList = new StringList();
		htmlOutputFullFileNameList = new StringList();
		htmlOutputFileList = new StringList();
		sqlList = new SQLInfoList();
    }
	/** html 파일 이름을 저장하는 함수이다.
	 */
	public void addHtmlName(String hName) {
		htmlList.insert(hName);
	}
	/** 유니크한 오퍼레이터들을 누적하는 함수이다.
	 */
	public void addUniqueOperators(StringList more) {
		String anOp = more.getFirst();
		while(anOp != null) {
			_uniqueOperators.insert(anOp);
			anOp = more.getNext();
		}
	}
	/** 유니크한 오퍼레이터들을 누적하는 함수이다.
	 */
	public void addUniqueOperands(StringList more) {
		String anOp = more.getFirst();
		while(anOp != null) {
			_uniqueOperands.insert(anOp);
			anOp = more.getNext();
		}
	}
	/** _classVarRefs 데이타들을 모으는 함수이다.
	 */
	public void collectVarRefs(VarRefBlock varRefs) {
		if (varRefs == null) {
			varRefs = _classVarRefs;
		}
		AttributeInfoNode aVarInfo = _attributes.getFirst();
		while (aVarInfo != null) {
			String name = aVarInfo.getName();
			String type = aVarInfo.getType();
			VarRefItem newItem = new VarRefItem(name,type);
			newItem.setVarPtr(aVarInfo);
			varRefs.insert(newItem);
			aVarInfo = _attributes.getNext();
		}
		Object obj = _superClasses.getFirst();
		while (obj != null) {
			if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				ClassInfoNode node = (ClassInfoNode)obj;
				node.collectVarRefs(varRefs);
			}
			obj = _superClasses.getNext();
		}
	}
	/** 클래스의 내용이 비어있는가를 확인하는 함수이다.
	 */
	public boolean isEmptyClass() {
		if (_attributes.nOfList() > 0) return false;
		if (_methods.nOfList() > 0) return false;
		return true;
	}
	/** _classVarRefs에 대한 읽기 access 함수이다.
	 */
	public VarRefBlock getClassVarRefs() {
		return _classVarRefs;
	}
	/** _attributes에 대한 읽기 access 함수이다.
	 */
	public AttributeList getAttributes() {
		return _attributes;
	}
	/** _attributes에 대한 읽기 access 함수이다.
	 */
	public MethodList getMethods() {
		return _methods;
	}
	/** _interfaceFlag에 대한 쓰기 access 함수이다.
	 */
	public void setInterfaceFlag() {
		_interfaceFlag = true;
	}
	/** _interfaceFlag에 대한 읽기 access 함수이다.
	 */
	public boolean interfaceFlag() {
		return _interfaceFlag;
	}
	/** _servletFlag에 대한 쓰기 access 함수이다.
	 */
	public void setServletFlag() {
		_servletFlag = true;
	}
	/** _servletFlag에 대한 읽기 access 함수이다.
	 */
	public boolean servletFlag() {
		return _servletFlag;
	}
	/** 파일 이름을 설정하는 함수이다.
	 */
	public void setFileName(String fileName) {
		_fileName = fileName;
	}
	/** 파일 이름에 대한 access 함수이다.
	 */
    public String fileName() {
		return new String(_fileName);
    }
	/** 패키지 이름을 설정하는 함수이다.
	 */
	public void setPackageName(String packageName) {
		_packageName = packageName;
	}
	/** 패키지 이름에 대한 access 함수이다.
	 */
    public String packageName() {
		return new String(_packageName);
    }
  	/** 클래스 이름에 대한 access 함수이다.
	 */
	public String className() {
		return new String(_className);
    }
  	/** 패키지 이름을 포함하는 클래스 이름에 대한 access 함수이다.
	 */
    public String fullClassName() {
		if (_packageName.length() == 0) return _className;
		else return new String(_packageName + "." + _className);
	}
  	/** super class list에 대한 access 함수이다.
	 */
	public ClassInfoNodeList getSuperClasses() {
		return _superClasses;
	}
	/** 슈퍼 클래스 이름을 추가하는 함수이다. 이 이름은 후에 클래스 정보 노드의
	 * 레퍼런스로 변경된다.
	 */
    public void addSuperClassName(String superName) {
		_superClasses.insert(superName);
    }
	/** 슈퍼 클래스 이름을 리턴하는 함수이다.
	 */
	public String getSuperClassName() {
		Object obj = _superClasses.getFirst();
		if (obj == null) {
			// error case
			return new String("Object");
		}
		if (obj.getClass().getName().equals("java.lang.String")) {
			return (String)obj;
		} else if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			return (String)((ClassInfoNode)obj).className();
		} else {
			// error case
			return new String("Object");
		}
	}
	/** 슈퍼 클래스 이름들을 클래스 정보 노드 레퍼런스로 변경하는 함수이다. 
	 */
	public void replaceSuperClassNamesToClassInfoNodes(ClassInfoNodeList classInfoNodes) {
		Object obj = _superClasses.getFirst();
		while (obj != null) {
			if (obj.getClass().getName().equals("java.lang.String")) {
				boolean replaced = false;
				Object ptr = classInfoNodes.getFirst();
				while (ptr != null) {
					if (ptr.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode") && 
						((ClassInfoNode)ptr).className().equals((String)obj)) {
//						System.out.println((String)obj);
						_superClasses.replaceStringToNodeRef(obj,ptr);
						replaced = true;
						break;
					} else if (ptr.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode") && 
						((ClassInfoNode)ptr).fullClassName().equals((String)obj)) {
//						System.out.println((String)obj);
						_superClasses.replaceStringToNodeRef(obj,ptr);
						replaced = true;
						break;
					}
					ptr = classInfoNodes.getNext();
				}
				if (replaced) {
					obj = _superClasses.getFirst();
				} else {
					obj = _superClasses.getNext();
				}
			} else {
				obj = _superClasses.getNext();
			}
		}
	}
	/** 한개의 데이터 멤버에 대한 정보를 추가하는 함수이다.
	 */
	public void addDataMember(ParseTreeNode1 dataMember) {
		if (dataMember == null) return;
		ParseTreeNode1 nameNode = dataMember;
		ParseTreeNode1 typeNode = nameNode.siblings;
		ParseTreeNode1 defaultValueNode = typeNode.siblings;
		ParseTreeNode1 modifierNode = defaultValueNode.siblings;
		AttributeInfoNode dataNode = new AttributeInfoNode(nameNode.value);
		dataNode.setType(typeNode.value);
		dataNode.setDefaultValue(defaultValueNode.value);
		// modifier 첫번째 문자에 visibility가 있다.
		dataNode.setVisibility(modifierNode.value);
		dataNode.setModifierString(modifierNode.value);
		dataNode.makeUMLString();
		_attributes.insert(dataNode);

		String orgName = dataNode.getName();
		String pureName = "";
		StringTokenizer st = new StringTokenizer(orgName,"[");
		if (st.hasMoreElements()) {
			pureName = (String)st.nextElement();
		} else {
			pureName = orgName;
		}
		dataMembers.insert(pureName);
	}
	/** 한개의 멤버 함수에 대한 정보를 추가하는 함수이다.
	 */
	public void addMemberFunction(ParseTreeNode1 memberFunction) {
		if (memberFunction == null) return;
		ParseTreeNode1 nameNode = memberFunction;
		ParseTreeNode1 typeNode = nameNode.siblings;
		ParseTreeNode1 modifierNode = typeNode.siblings;
		ParseTreeNode1 exceptionNode = modifierNode.siblings;
		MethodInfoNode functionNode = new MethodInfoNode(nameNode.value);
		functionNode.setType(typeNode.value);
		// modifier 첫번째 문자에 visibility가 있다.
		functionNode.setVisibility(modifierNode.value);
		functionNode.setModifierString(modifierNode.value);
		functionNode.addExceptions(exceptionNode.children);
		functionNode.makeArgList(nameNode.children);
		functionNode.makeUMLString();
		_methods.insert(functionNode);
	}
	/** 패스2의 파스트리 노드를 이용하여 메소드 정보를 찾는 함수이다.
	 */
	public MethodInfoNode findMethodInfo(ParseTreeNode2 methodHeaderNode) {
		if (methodHeaderNode == null) return null;
		ParseTreeNode2 nameNode = methodHeaderNode;
		ParseTreeNode2 typeNode = nameNode.siblings;
		ParseTreeNode2 modifierNode = typeNode.siblings;
		ParseTreeNode2 exceptionNode = modifierNode.siblings;
		ParseTreeNode2 args = nameNode.children;
		StringList argTypes = new StringList();
		ParseTreeNode2 argNode = args;
		while(argNode != null) {
			argTypes.addTail(new String(argNode.siblings.value));
			argNode = argNode.children;
		}
		String typeString = typeNode.value;
		if (typeNode.value == null) {
			typeString = "";
		}
		return _methods.findMethodInfo(nameNode.value,typeString,argTypes);
	}
	/** return type을 모르는 상태에서 메소드 정보를 찾는 함수이다.
	 */
	public MethodInfoNode tryToFindMethodInfoNoReturnType(String functionName,StringList argTypes) {
		MethodInfoNode mNode = tryToFindExactMethodInfoNoReturnType(functionName,argTypes);
		if (mNode != null) {
			return mNode;
		}
		mNode = tryToFindMethodInfoNArgNoReturnType(functionName,argTypes);
		if (mNode != null) {
			return mNode;
		}
		return mNode;
	}
	/** return type을 모르는 상태에서 인자 갯수만 이용하여 메소드 정보를 찾는 함수이다.
	 */
	public MethodInfoNode tryToFindMethodInfoNArgNoReturnType(String functionName,StringList argTypes) {
		MethodInfoNode mNode = _methods.findMethodInfoNArgNoReturnType(functionName,argTypes);
		if (mNode != null) return mNode;
		Object obj = _superClasses.getFirst();
		while (obj != null) {
			if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				ClassInfoNode node = (ClassInfoNode)obj;
				mNode = node.tryToFindMethodInfoNArgNoReturnType(functionName,argTypes);
				if (mNode != null) return mNode;
			}
			obj = _superClasses.getNext();
		}
		return null;
	}
	/** return type을 모르는 상태에서 정확한 메소드 정보를 찾는 함수이다.
	 */
	public MethodInfoNode tryToFindExactMethodInfoNoReturnType(String functionName,StringList argTypes) {
		MethodInfoNode mNode = _methods.findExactMethodInfoNoReturnType(functionName,argTypes);
		if (mNode != null) return mNode;
		Object obj = _superClasses.getFirst();
		while (obj != null) {
			if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				ClassInfoNode node = (ClassInfoNode)obj;
				mNode = node.tryToFindExactMethodInfoNoReturnType(functionName,argTypes);
				if (mNode != null) return mNode;
			}
			obj = _superClasses.getNext();
		}
		return null;
	}
	/** 멤버들을 UML 스타일로 프린트하는 함수이다. 디버깅이 목적이다.
	 */
	public void printMembers() {
		AttributeInfoNode data = _attributes.getFirst();
		while (data != null) {
			System.out.println(data.getUMLString());
			data = _attributes.getNext();
		}
		MethodInfoNode func = _methods.getFirst();
		while (func != null) {
			System.out.println(func.getUMLString());
			func = _methods.getNext();
		}
	}
	/** implements ... 에 적힌 상위 인터페이스 이름들을 돌려주는 함수이다.
	 */
	public StringList getSuperInterfaceNames() {
		StringList names = new StringList();
		Object obj = _superClasses.getFirst();
		obj = _superClasses.getNext();
		if (obj == null) return names;
		while (obj != null) {
			if (obj.getClass().getName().equals("java.lang.String")) {
				names.insert((String)obj);
			} else if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				names.insert((String)((ClassInfoNode)obj).className());
			}
			obj = _superClasses.getNext();
		}
		return names;
	}
	/** implements ... 를 프린트하는 함수이다. 디버깅이 목적이다.
	 */
	public void printImplementedInterfaces() {
		Object obj = _superClasses.getFirst();
		obj = _superClasses.getNext();
		if (obj == null) return;
		System.out.print("implements ");
		while (obj != null) {
			if (obj.getClass().getName().equals("java.lang.String")) {
				System.out.print(" " + (String)obj);
			} else if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				System.out.print(" " + (String)((ClassInfoNode)obj).className());
			}
			obj = _superClasses.getNext();
		}
		System.out.println("");
	}
	/** 어떤 스트링이 java.io.* 의 중요 클래스 이름에 속하는 가를 확인하는 함수이다.
	 */
	private static boolean isJavaIOType(String typeName) {
		if (typeName.equals("DataInput") ||
			typeName.equals("DataOutput") || 
			typeName.equals("Externalizable") || 
			typeName.equals("FileFilter") ||
			typeName.equals("FilenameFilter") ||
			typeName.equals("ObjectInput") ||
			typeName.equals("ObjectInputValidation") || 
			typeName.equals("ObjectOutput") || 
			typeName.equals("ObjectStreamConstants") || 
			typeName.equals("Serializable") || 
			typeName.equals("BufferedInputStream") || 
			typeName.equals("BufferedOutputStream") || 
			typeName.equals("BufferedReader") || 
			typeName.equals("BufferedWriter") || 
			typeName.equals("ByteArrayInputStream") || 
			typeName.equals("ByteArrayOutputStream") || 
			typeName.equals("CharArrayReader") || 
			typeName.equals("CharArrayWriter") || 
			typeName.equals("DataInputStream") || 
			typeName.equals("DataOutputStream") || 
			typeName.equals("File") || 
			typeName.equals("FileDescriptor") || 
			typeName.equals("FileInputStream") || 
			typeName.equals("FileOutputStream") || 
			typeName.equals("FilePermission") || 
			typeName.equals("FileReader") || 
			typeName.equals("FileWriter") || 
			typeName.equals("FilterInputStream") || 
			typeName.equals("FilterOutputStream") || 
			typeName.equals("FilterReader") || 
			typeName.equals("FilterWriter") || 
			typeName.equals("InputStream") || 
			typeName.equals("InputStreamReader") || 
			typeName.equals("LineNumberInputStream") || 
			typeName.equals("LineNumberReader") || 
			typeName.equals("ObjectInputStream") || 
			typeName.equals("ObjectInputStream.GetField") || 
			typeName.equals("ObjectOutputStream") || 
			typeName.equals("ObjectOutputStream.PutField") || 
			typeName.equals("ObjectStreamClass") || 
			typeName.equals("ObjectStreamField") || 
			typeName.equals("OutputStream") || 
			typeName.equals("OutputStreamWriter") || 
			typeName.equals("PipedInputStream") || 
			typeName.equals("PipedOutputStream") || 
			typeName.equals("PipedReader") || 
			typeName.equals("PipedWriter") || 
			typeName.equals("PrintStream") || 
			typeName.equals("PrintWriter") || 
			typeName.equals("PushbackInputStream") || 
			typeName.equals("PushbackReader") || 
			typeName.equals("RandomAccessFile") || 
			typeName.equals("Reader") || 
			typeName.equals("SequenceInputStream") || 
			typeName.equals("SerializablePermission") || 
			typeName.equals("StreamTokenizer") || 
			typeName.equals("StringBufferInputStream") || 
			typeName.equals("StringReader") || 
			typeName.equals("StringWriter") || 
			typeName.equals("Writer") || 
			typeName.equals("CharConversionException") || 
			typeName.equals("EOFException") || 
			typeName.equals("FileNotFoundException") || 
			typeName.equals("InterruptedIOException") || 
			typeName.equals("InvalidClassException") || 
			typeName.equals("InvalidObjectException") || 
			typeName.equals("IOException") || 
			typeName.equals("NotActiveException") || 
			typeName.equals("NotSerializableException") || 
			typeName.equals("ObjectStreamException") || 
			typeName.equals("OptionalDataException") || 
			typeName.equals("StreamCorruptedException") || 
			typeName.equals("SyncFailedException") || 
			typeName.equals("UnsupportedEncodingException") ||
			typeName.equals("UTFDataFormatException") || 
			typeName.equals("WriteAbortedException")) {
			return true;
		}
		return false;
	}
	/** 어떤 스트링이 java.lang.* 의 중요 클래스 이름에 속하는 가를 확인하는 함수이다.
	 */
	private static boolean isJavaLangType2(String typeName) {
		if (typeName.equals("Cloneable") ||
			   typeName.equals("Comparable") || 
			   typeName.equals("Runnable") ||  
			   typeName.equals("Subset") ||  
			   typeName.equals("UnicodeBlock") || 
			   typeName.equals("Class") || 
			   typeName.equals("ClassLoader") || 
			   typeName.equals("Compiler") || 
			   typeName.equals("InheritableThreadLocal") || 
			   typeName.equals("Math") || 
			   typeName.equals("Number") || 
			   typeName.equals("Object") || 
			   typeName.equals("Package") || 
			   typeName.equals("Process") || 
			   typeName.equals("Runtime") || 
			   typeName.equals("RuntimePermission") || 
			   typeName.equals("SecurityManager") || 
			   typeName.equals("StrictMath") || 
			   typeName.equals("System") || 
			   typeName.equals("Thread") || 
			   typeName.equals("ThreadGroup") || 
			   typeName.equals("ThreadLocal") || 
			   typeName.equals("Throwable") || 
			   typeName.equals("ArithmeticException") || 
			   typeName.equals("ArrayIndexOutOfBoundsException") || 
			   typeName.equals("ArrayStoreException") || 
			   typeName.equals("ClassCastException") || 
			   typeName.equals("ClassNotFoundException") || 
			   typeName.equals("CloneNotSupportedException") || 
			   typeName.equals("Exception") || 
			   typeName.equals("IllegalAccessException") || 
			   typeName.equals("IllegalArgumentException") || 
			   typeName.equals("IllegalMonitorStateException") || 
			   typeName.equals("IllegalStateException") || 
			   typeName.equals("IllegalThreadStateException") || 
			   typeName.equals("IndexOutOfBoundsException") || 
			   typeName.equals("InstantiationException") || 
			   typeName.equals("InterruptedException") || 
			   typeName.equals("NegativeArraySizeException") || 
			   typeName.equals("NoSuchFieldException") || 
			   typeName.equals("NoSuchMethodException") || 
			   typeName.equals("NullPointerException") || 
			   typeName.equals("NumberFormatException") || 
			   typeName.equals("RuntimeException") || 
			   typeName.equals("SecurityException") || 
			   typeName.equals("StringIndexOutOfBoundsException") || 
			   typeName.equals("UnsupportedOperationException") || 
			   typeName.equals("AbstractMethodError") || 
			   typeName.equals("ClassCircularityError") || 
			   typeName.equals("ClassFormatError") || 
			   typeName.equals("Error") || 
			   typeName.equals("ExceptionInInitializerError") || 
			   typeName.equals("IllegalAccessError") || 
			   typeName.equals("IncompatibleClassChangeError") || 
			   typeName.equals("InstantiationError") || 
			   typeName.equals("InternalError") || 
			   typeName.equals("LinkageError") || 
			   typeName.equals("NoClassDefFoundError") || 
			   typeName.equals("NoSuchFieldError") || 
			   typeName.equals("NoSuchMethodError") || 
			   typeName.equals("OutOfMemoryError") || 
			   typeName.equals("StackOverflowError") || 
			   typeName.equals("ThreadDeath") || 
			   typeName.equals("UnknownError") || 
			   typeName.equals("UnsatisfiedLinkError") || 
			   typeName.equals("UnsupportedClassVersionError") || 
			   typeName.equals("VerifyError") || 
			   typeName.equals("VirtualMachineError")) {
			return true;
		} 
		return false;
	}
	/** 어떤 스트링이 java.lang.* 의 기본 클래스 이름에 속하는 가를 확인하는 함수이다.
	 */
	private static boolean isJavaLangType1(String typeName) {
		if (typeName.equals("Void") ||
			   typeName.equals("Boolean") ||
			   typeName.equals("Byte") ||
			   typeName.equals("Character") ||
			   typeName.equals("String") ||
			   typeName.equals("StringBuffer") ||
			   typeName.equals("Short") ||
			   typeName.equals("Integer") ||
			   typeName.equals("Long") ||
			   typeName.equals("Number") ||
			   typeName.equals("Float") ||
			   typeName.equals("Double")) {
			return true;
		}
		return false;
	}
	/** 어떤 스트링이 java.lang.* 의 클래스 이름에 속하는 가를 확인하는 함수이다.
	 */
	private static boolean isJavaLangType(String typeName) {
		if (isJavaLangType1(typeName) == true) return true;
		if (isJavaLangType2(typeName) == true) return true;
		return false;
	}
	/** DAC 값을 구하는 함수이다.
	 */
	private int getDataAbstractionCoupling() {
		StringList refTypes = new StringList();
		AttributeInfoNode aNode = _attributes.getFirst();
		while (aNode != null) {
			String typeNameAll = aNode.getType();
			String typeName;
			StringTokenizer st = new StringTokenizer(typeNameAll,"[");
			if (st.hasMoreElements()) {
				typeName = (String)st.nextElement();
			} else {
				typeName = typeNameAll;
			}
			if (typeName.equals("boolean") == false &&
				typeName.equals("byte") == false &&
				typeName.equals("char") == false &&
				typeName.equals("short") == false &&
				typeName.equals("int") == false &&
				typeName.equals("long") == false &&
				typeName.equals("float") == false &&
				typeName.equals("double") == false &&
				isJavaLangType(typeName) == false) {
				refTypes.insert(typeName);
			}
			aNode = _attributes.getNext();
		}
		return refTypes.nOfList();
	}
	/** number of attributes를 구하는 함수이다.
	 * 정적이면서 파이널 데이터 멤버는 카운트하지 않는다.
	 */
	private int getNOA() {
		int n = 0;
		AttributeInfoNode aNode = _attributes.getFirst();
		while (aNode != null) {
			if (aNode.isStaticFinal() == false) n++;
			aNode = _attributes.getNext();
		}
		return n;
	}
	/** attribute complexity를 구하는 함수이다.
	 */
	private int getAttributeComplexity() {
		int n = 0;
		AttributeInfoNode aNode = _attributes.getFirst();
		while (aNode != null) {
			String typeName = aNode.getType();
			if (aNode.isArrayType() == true) {
				n = n + 3;
			} else if (typeName.equals("boolean") ||
					   typeName.equals("byte") ||
					   typeName.equals("char") ||
					   typeName.equals("short") ||
					   typeName.equals("int") ||
					   typeName.equals("long")) {
				n = n + 1;
			} else if (typeName.equals("float") ||
					   typeName.equals("double")) {
				n = n + 2;
			} else if (isJavaLangType1(typeName) == true) {
				n = n + 3;
			} else if (isJavaLangType2(typeName) == true) {
				n = n + 5; 
			} else if (typeName.equals("Vector")) {
				n = n + 7;
			} else {
				n = n + 9;
			}
			aNode = _attributes.getNext();
		}
		return n;
	}
	/** 생성자의 갯수를 세는 함수이다.
	 */
	private int getNOfConstructors() {
		int n = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			if (_className.equals(mNode.getName()) == true) {
				n++;
			}
			mNode = _methods.getNext();
		}
		return n;
	}
	/** FO를 구하는 함수이다.
	 */
	private int getFanOut() {
		StrRelation depends = ServletParser.getDependencies();
		if (depends == null) return 0;

		StringList supers = new StringList();
		getSuperClassNames(supers);
		supers.insert(_className);
		StringList usedClassList = new StringList();
		StrRelItem item = depends.getFirst();
		while(item != null) {
			if (_className.equals(item.first()) &&
				supers.inList(item.second()) == false) {
				usedClassList.insert(item.second());
			}
			item = depends.getNext();
		}
		return usedClassList.nOfList();
	}
	/** CBO를 구하는 함수이다.
	 */
	private int getCouplingBetweenObjects() {
		StrRelation depends = ServletParser.getDependencies();
		if (depends == null) return 0;

		StringList supers = new StringList();
		getSuperClassNames(supers);
		supers.insert(_className);

		StringList usedClassList = new StringList();
		StrRelItem item = depends.getFirst();
		while(item != null) {
			if (_className.equals(item.first()) &&
				supers.inList(item.second()) == false) {
				if (isJavaLangType(item.second()) == false) {
					usedClassList.insert(item.second());
				}
			}
			item = depends.getNext();
		}
		return usedClassList.nOfList();
	}
	/** NOC를 구하는 함수이다. 이를 구하기 위해 ServletParser 클래스의 generalization list를 이용한다.
	 */
	private int getNOCFromGeneralizationList() {
		StrRelation gentions = ServletParser.getGeneralizations();
		if (gentions == null) return 0;
		StringList subClassList = new StringList();
		StrRelItem item = gentions.getFirst();
		while(item != null) {
			if (_className.equals(item.first())) {
				subClassList.insert(item.second());
			}
			item = gentions.getNext();
		}
		return subClassList.nOfList();
	}
	/** 인자 수의 최대 값을 찾는 함수이다.
	 */
	private int getMaxNoOfParameters() {
		int max = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			ParameterList pList = mNode.getParameters();
			int n = pList.nOfList();
			if (n > max) max = n;
			mNode = _methods.getNext();
		}
		return max;
	}
	/** 함수가 nesting된 블럭의 최대 깊이를 구하는 함수이다.
	 */
	private int getMaxNoOfLevels() {
		int maxLevel = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			InstructionBlock mBlock = mNode.iBlock;
			int level = mBlock.getLevel();
			if (level > maxLevel) maxLevel = level;
			mNode = _methods.getNext();
		}
		return maxLevel;
	}
	/** 가시성의 퍼센트를 구하는 함수이다.
	 */
	private void getVisibilityPercentage(int visibilityPercentage[]) {
		int IPac = 0;
		int IPri = 1;
		int IPro = 2;
		int IPub = 3;

		int nPac = 0;
		int nPub = 0;
		int nPro = 0;
		int nPri = 0;

		int nTotal = 0;

		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			nTotal++;
			String v = mNode.getAccessSpecifier();
			if (v.length() == 0) nPac++;
			else if (v.equals("public")) nPub++;
			else if (v.equals("protected")) nPro++;
			else if (v.equals("private")) nPri++;
			mNode = _methods.getNext();
		}
		AttributeInfoNode aNode = _attributes.getFirst();
		while (aNode != null) {
			nTotal++;
			String v = aNode.getAccessSpecifier();
			if (v.length() == 0) nPac++;
			else if (v.equals("public")) nPub++;
			else if (v.equals("protected")) nPro++;
			else if (v.equals("private")) nPri++;
			aNode = _attributes.getNext();
		}
		if (nTotal > 0) {
			visibilityPercentage[IPac] = 100 * nPac / nTotal;
			visibilityPercentage[IPri] = 100 * nPri / nTotal;
			visibilityPercentage[IPro] = 100 * nPro / nTotal;
			visibilityPercentage[IPub] = 100 * nPub / nTotal;
		} else {
			visibilityPercentage[IPac] = 0;
			visibilityPercentage[IPri] = 0;
			visibilityPercentage[IPro] = 0;
			visibilityPercentage[IPub] = 0;
		}
	}
	/** _loc에 대한 write access 함수이다.
	 */
	public void setLoc(int n) {
		_loc = n;
	}
	/** _tcr에 대한 write access 함수이다.
	 */
	public void setTcr(int nLinesExcludingComments,int nJavaComments, int nOrdinaryComments) {
		_tcr = (nJavaComments + nOrdinaryComments)*100 / nLinesExcludingComments;
	} 
	/** _cr에 대한 write access 함수이다.
	 */
	public void setCr(int nLines,int nJavaComments, int nOrdinaryComments) {
		_cr = (nJavaComments + nOrdinaryComments)*100 / nLines;
	} 
	/** _nois에 대한 write access 함수이다.
	 */
	public void setNois(int n) {
		_nois = n;
	}
	/** 클래스의 cyclomatic complexity를 구하는 함수이다.
	 */
	private int getClassCyclomaticComplexity() {
		int cc = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
//			cc = cc + mNode.getCyclomaticComplexity(); 
			// 함수별 CC 값은 10을 최대값으로 가정하고 있음
			int temp = mNode.getCyclomaticComplexity();
			if (temp > 10) {
				cc = cc + 10;
			} else { 
				cc = cc + temp; 
			}
			mNode = _methods.getNext();
		}
		if (cc == 0) cc = 1;
		return cc;
	}
	/** 클래스에 속하는 함수 중 최대 cyclomatic complexity를 구하는 함수이다.
	 * i.e. size of operation = cyclomatic complexity
	 */
	private int getMaxSizeOfOperations () {
		int ccMax = 1;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			int cc = mNode.getCyclomaticComplexity(); 
			if (cc > ccMax) ccMax = cc;
			mNode = _methods.getNext();
		}
		return ccMax;
	}
	/** 클래스의 WMPC1를 구하는 함수이다. weight는 각 함수의 cyclomatic complexity이다.
	 */
	private int getWMPC1() {
		int wmpc1 = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			if (_className.equals(mNode.getName()) == false) {
				wmpc1 = wmpc1 + mNode.getCyclomaticComplexity() + 1; // 마지막 1은 메소드 수 
			}
			mNode = _methods.getNext();
		}
		return wmpc1;
	}
	/** 클래스의 WMPC2를 구하는 함수이다. weight는 각 함수의 parameter 수 이다.
	 */
	private int getWMPC2() {
		int wmpc2 = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			if (_className.equals(mNode.getName()) == false) {
				ParameterList pList = mNode.getParameters();
				int n = pList.nOfList();
				wmpc2 = wmpc2 + n + 1; // 마지막 1은 메소드 수
			}
			mNode = _methods.getNext();
		}
		return wmpc2;
	}
	/** 상속 깊이를 구하는 함수이다.
	 */
	private int getDepthOfInheritanceHierarchy() {
		if (_superClasses.nOfList() == 0) return 0;
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode parent = (ClassInfoNode)obj;
			return parent.getDepthOfInheritanceHierarchy() + 1;
		} 
		return 1;
	}
	/** 부모 클래스 이름들을 구하는 함수이다.
	 */
	private void getSuperClassNames(StringList supers) {
		if (_superClasses.nOfList() == 0) {
			supers.insert("Object");
			return;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode parent = (ClassInfoNode)obj;
			supers.insert(parent._className);
			parent.getSuperClassNames(supers);
		} else if (obj.getClass().getName().equals("java.lang.String")) {
			supers.insert((String)obj);
		}
		return;
	}
	/** 상속된 메소드의 수를 구하는 함수이다.
	 */
	private int getInheritedMethods() {
		if (_superClasses.nOfList() == 0) {
			return 0;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode parent = (ClassInfoNode)obj;
			int n = 0;
			MethodInfoNode mNode = parent.getMethods().getFirst();
			while(mNode != null) {
				if (mNode.getName().equals(_className) == false &&
					mNode.getVisibility().equals("-") == false) n++;
				mNode = parent.getMethods().getNext();
			}
			return n + parent.getInheritedMethods();
		} else if (obj.getClass().getName().equals("java.lang.String")) {
			return 0;
		}
		return 0;
	}
	/** 상속된 어트리뷰트의 수를 구하는 함수이다.
	 */
	private int getInheritedAttributes() {
		if (_superClasses.nOfList() == 0) {
			return 0;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode parent = (ClassInfoNode)obj;
			int n = 0;
			AttributeInfoNode aNode = parent.getAttributes().getFirst();
			while(aNode != null) {
				if (aNode.getVisibility().equals("-") == false) n++;
				aNode = parent.getAttributes().getNext();
			}
			return n + parent.getInheritedAttributes();
		} else if (obj.getClass().getName().equals("java.lang.String")) {
			return 0;
		}
		return 0;
	}
	/** 인자로 주어진 함수 시그니쳐가 오버라이드되어 있는가를 확인하는함수이다.
	 */
	private boolean hasFunctionSignature(String funcSignature) {
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			String sig = mNode.getSignature();
			if (sig.equals(funcSignature)) return true;
			mNode = _methods.getNext();
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode superClassNode = (ClassInfoNode)obj;
			return superClassNode.hasFunctionSignature(funcSignature);
		} 
		return false;		
	}
	/** 이 클래스의 멤버 함수 중 추가된 함수의 갯수를 구하는 함수이다.
	 * 상위 클래스가 없는 경우나 상위 클래스가 라이브러리 클래스인 경우 0 으로 처리한다.
	 * 함수의 추가 여부는 함수 이름 뿐아니라 시그니쳐가 같은가도 고려한다. 
	 */
	private int countNumberOfAddedMethods() {
		if (_superClasses.nOfList() == 0) {
			return 0;
		}
		int addedMethodCount = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			String sig = mNode.getSignature();
			Object obj = _superClasses.getFirst();
			if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				ClassInfoNode superClassNode = (ClassInfoNode)obj;
				if (superClassNode.hasFunctionSignature(sig) == false) {
					addedMethodCount++;
				}
			} 		
			mNode = _methods.getNext();
		}
		return addedMethodCount;
	}
	/** 슈퍼 클래스의 CBO값을 구하는 함수이다.
	 */
	private int getSuperCOB() {
		if (_superClasses.nOfList() == 0) {
			return 0;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode parent = (ClassInfoNode)obj;
			return parent._cbo + parent.getSuperCOB();
		} else if (obj.getClass().getName().equals("java.lang.String")) {
			return 0;
		}
		return 0;
	}
	/** 이 클래스랑 슈퍼 클래스의 메소드 이름들을 모으는 함수이다.
	 */
	private void collectThisAndSuperMethods(StringList allMethods) {
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			allMethods.insert(mNode.getName());
			mNode = _methods.getNext();
		}
		if (_superClasses.nOfList() == 0) {
			return;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode superNode = (ClassInfoNode)obj;
			superNode.collectThisAndSuperMethods(allMethods);
			return;
		} 
		return;		
	}
	/** 이 클래스의 멤버 함수 중 오버라이드된 함수의 갯수를 구하는 함수이다.
	 * 상위 클래스가 없는 경우나 상위 클래스가 라이브러리 클래스인 경우 0 으로 처리한다.
	 * 함수의 오버라이드 여부는 함수 이름 뿐아니라 시그니쳐가 같은가도 고려한다. 
	 */
	private int countNumberOfOverriddenMethods() {
		if (_superClasses.nOfList() == 0) {
			return 0;
		}
		int overriddenMethodCount = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			String sig = mNode.getSignature();
			Object obj = _superClasses.getFirst();
			if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
				ClassInfoNode superClassNode = (ClassInfoNode)obj;
				if (superClassNode.hasFunctionSignature(sig)) {
					overriddenMethodCount++;
				}
			} 		
			mNode = _methods.getNext();
		}
		return overriddenMethodCount;
	}
	/** Response For Class 값을 구하는 함수이다.
	 * RFC = number of methods + remote method calls
	 */
	private int getResponseForClass() {
		int nMethods = _methods.nOfList();
		int rfc = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			mNode.calcRemoteMethodCalls(_thisAndSuperMethods);
			rfc = rfc + mNode.getNumberOfUniqueRemoteCalls();
			mNode = _methods.getNext();
		}
		rfc = nMethods + rfc;
		return rfc;
	}
	/** Number of RemoteMethods를 구하는 함수이다.
	 */
	private int getNumberOfRemoteMethods() {
		StringList remoteMethods = new StringList();
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			mNode.gatherRemoteMethodCalls(remoteMethods,_thisAndSuperMethods);
			mNode = _methods.getNext();
		}
		return remoteMethods.nOfList();
	}
	/** 오퍼랜드 수 메트릭 값을 구하는 함수이다.
	 */
	private int getNOperands() {
		int sum = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			sum = sum + mNode.getNOperands();
			mNode = _methods.getNext();
		}
		return sum;
	}
	/** 오퍼레이터 수 메트릭 값을 구하는 함수이다.
	 */
	private int getNOperators() {
		int sum = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			sum = sum + mNode.getNOperators();
			mNode = _methods.getNext();
		}
		return sum;
	}
	/** 유니크한 오퍼랜드 수 메트릭 값을 구하는 함수이다.
	 */
	private int getNUniqueOperands() {
		int sum = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			sum = sum + mNode.getNUniqueOperands();
			mNode = _methods.getNext();
		}
		return sum;
	}
	/** 유니크한 오퍼레이터 수 메트릭 값을 구하는 함수이다.
	 */
	private int getNUniqueOperators() {
		int sum = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			sum = sum + mNode.getNUniqueOperators();
			mNode = _methods.getNext();
		}
		return sum;
	}
	/** LOCOM1 을 구하는 함수이다.
	 * Lack Of Cohesion Of Method 1
	 */
	private int getLOCOM1() {
		int P = 0;
		int Q = 0;
		
		MethodList copiedList = new MethodList();
		copiedList.copy(_methods);
		MethodInfoNode node1 = _methods.getFirst();
		while (node1 != null) {
			MethodInfoNode node2 = copiedList.getFirst();
			while(node2 != null) {
				if (node1 != node2) {
					StringList list1 = node1.usedDataMembers;
					StringList list2 = node2.usedDataMembers;
					boolean disjointFlag = true;
					String s = list1.getFirst();
					while(s != null) {
						if (list2.inList(s) == true) {
							disjointFlag = false;
							break;
						}
						s = list1.getNext();
					}
					if (disjointFlag == true) P++;
					else Q++;
				}
				node2 = copiedList.getNext();
			}
			node1 = _methods.getNext();
		}
		P = P/2;
		Q = Q/2;
		if (P > Q) return P-Q;
		else return 0;
	}
	/** LOCOM2 을 구하는 함수이다.
	 * Lack Of Cohesion Of Method 2
	 */
	private int getLOCOM2() {
		int nData = dataMembers.nOfList();
		if (nData == 0) return 0;
		int totalPercent = 0;
		int mMethods = _methods.nOfList();
		if (mMethods == 0) return 100;
		String aMember = dataMembers.getFirst();
		while(aMember != null) {
			int nAttributeNotUsed = 0;
			MethodInfoNode mNode = _methods.getFirst();
			while (mNode != null) {
				if (mNode.usedDataMembers.inList(aMember) == false) {
					nAttributeNotUsed++;
				}
				mNode = _methods.getNext();
			}
			int unusedPercent = nAttributeNotUsed*100/mMethods;
			totalPercent = totalPercent + unusedPercent;
			aMember = dataMembers.getNext();
		}
		return totalPercent/nData;
	}
	/** LOCOM3 을 구하는 함수이다.
	 * Lack Of Cohesion Of Method 3
	 */
	private int getLOCOM3() {
		int nData = dataMembers.nOfList();
		if (nData == 0) return 0;
		int A = nData;
		int mMethods = _methods.nOfList();
		if (mMethods == 1) return 0;
		int M = mMethods;
		int EMA = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			EMA = EMA + mNode.usedDataMembers.nOfList();
			mNode = _methods.getNext();
		}
		// calculate 100*(EMA/A-M)/(1-M)
		int t1 = (int)((double)EMA/(double)A);
		if (t1 == 1) return 100;
		int t2 = 100*(t1-M);
		int t3 = (int)((double)t2/(double)(1.0-(double)M));
		return t3;
	}
	/** 조상들 클래스에게 후손의 수를 증가시키는 함수이다.
	 */
	private void incrNumberOfDescendantsToParent() {
		if (_superClasses.nOfList() == 0) {
			return;
		}
		Object obj = _superClasses.getFirst();
		if (obj.getClass().getName().equals("parser.ServletParser.library.ClassInfoNode")) {
			ClassInfoNode superNode = (ClassInfoNode)obj;
			superNode._nDescendants++;
			superNode.incrNumberOfDescendantsToParent();
		} 
		return;
	}
	/** metrics data 를 모으는 함수이다.
	 */
	public void collectMetricsData(MetricsData data) {
		incrNumberOfDescendantsToParent();
		collectThisAndSuperMethods(_thisAndSuperMethods);
		data.setMetricsData(MetricsData.Iloc,_loc); // 0
		int noa = getNOA();
		data.setMetricsData(MetricsData.Inoa,noa); // 1
		data.setMetricsData(MetricsData.Inoc,1); // 2
		int nocon = getNOfConstructors();
		int noo = _methods.nOfList() - nocon;
		data.setMetricsData(MetricsData.Inocon,nocon); // 3
		data.setMetricsData(MetricsData.Inois,_nois); // 4
		data.setMetricsData(MetricsData.Inom,noa+noo); // 5
		data.setMetricsData(MetricsData.Inoo,noo); // 6
		data.setMetricsData(MetricsData.Ilocom1,getLOCOM1()); // 7
		data.setMetricsData(MetricsData.Ilocom2,getLOCOM2()); // 8
		data.setMetricsData(MetricsData.Ilocom3,getLOCOM3()); // 9
		data.setMetricsData(MetricsData.Iac,getAttributeComplexity()); // 10
		data.setMetricsData(MetricsData.Icc,getClassCyclomaticComplexity()); // 11
		data.setMetricsData(MetricsData.Inorm,getNumberOfRemoteMethods()); // 12
		data.setMetricsData(MetricsData.Irfc,getResponseForClass()); // 13
		data.setMetricsData(MetricsData.Iwmpc1,getWMPC1()); // 14
		data.setMetricsData(MetricsData.Iwmpc2,getWMPC2()); // 15
		data.setMetricsData(MetricsData.Icbo,_cbo = getCouplingBetweenObjects()); // 16
		data.setMetricsData(MetricsData.Idac,getDataAbstractionCoupling()); // 18
		data.setMetricsData(MetricsData.Ifo,getFanOut()); // 19

		int noprnd = getNOperands();
		int noprtr = getNOperators();
//		int nuoprnd = getNUniqueOperands();
		int nuoprnd = _uniqueOperands.nOfList();
//		int nuoprtr = getNUniqueOperators();
		int nuoprtr = _uniqueOperators.nOfList();
		if (nuoprnd == 0) nuoprnd = 1;
//		int hdiff = nuoprtr*noprnd/(2*nuoprnd);
		double temphdiff = (double)(nuoprtr*noprnd)/(double)(nuoprnd*nuoprnd);
		int hdiff = (int)(temphdiff+0.5);
		int hplen = noprtr+noprnd;
		int hpvoc = nuoprtr+nuoprnd;
		int hpvol = (int) ((double)hplen * ((double)Math.log((double)hpvoc) / (double)Math.log(2.0)));
		int heff = hdiff * hpvol;
		data.setMetricsData(MetricsData.Ihdiff,hdiff); // 20
		data.setMetricsData(MetricsData.Ihplen,hplen); // 21
		data.setMetricsData(MetricsData.Iheff,heff); // 22
		data.setMetricsData(MetricsData.Ihpvoc,hpvoc); // 23
		data.setMetricsData(MetricsData.Ihpvol,hpvol); // 24
		data.setMetricsData(MetricsData.Inoprnd,noprnd); // 25
		data.setMetricsData(MetricsData.Inoprtr,noprtr); // 26
		data.setMetricsData(MetricsData.Inuoprnd,nuoprnd); // 27
		data.setMetricsData(MetricsData.Inuoprtr,nuoprtr); // 28
		data.setMetricsData(MetricsData.Idoih,getDepthOfInheritanceHierarchy()); // 32
		// NOCC 값은 setNOCC()를 통해서 셋팅된다. 
		// 원래는 child class 수이지만 Together에서는 descendant 수를 이용하기 때문이다.
//		data.setMetricsData(MetricsData.Inocc,getNOCFromGeneralizationList()); // 34
		data.setMetricsData(MetricsData.Imnol,getMaxNoOfLevels()); // 35
		data.setMetricsData(MetricsData.Imnop,getMaxNoOfParameters()); // 36
		data.setMetricsData(MetricsData.Imsoo,getMaxSizeOfOperations()); // 37

		_noam = countNumberOfAddedMethods();
		data.setMetricsData(MetricsData.Inoam,_noam); // 38

		_noom = countNumberOfOverriddenMethods();
		data.setMetricsData(MetricsData.Inoom,_noom); // 39

		data.setMetricsData(MetricsData.Icr,_cr); // 41

		int IPac = 0;
		int IPri = 1;
		int IPro = 2;
		int IPub = 3;
		int visibilityPercentage[] = new int[4];
		getVisibilityPercentage(visibilityPercentage);

		data.setMetricsData(MetricsData.Ippkgm,visibilityPercentage[IPac]); // 42
		data.setMetricsData(MetricsData.Ipprivm,visibilityPercentage[IPri]); // 43
		data.setMetricsData(MetricsData.Ipprotm,visibilityPercentage[IPro]); // 44
		data.setMetricsData(MetricsData.Ippubm,visibilityPercentage[IPub]); // 45
		data.setMetricsData(MetricsData.Itcr,_tcr); // 46

		_numberOfAttributes = _attributes.nOfList();
		_numberOfInvisibleAttributes = 0;
		AttributeInfoNode aNode = _attributes.getFirst();
		while (aNode != null) {
			if (aNode.getVisibility().equals("-") || aNode.getVisibility().equals("#")) {
				_numberOfInvisibleAttributes++;
			}
			aNode = _attributes.getNext();
		}
		_numberOfMethods = 0;
		_numberOfInvisibleMethods = 0;
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			if (mNode.getName().equals(_className) == false) {
				_numberOfMethods++;
				if (mNode.getVisibility().equals("-") || mNode.getVisibility().equals("#")) {
					_numberOfInvisibleMethods++;
				}
			}
			mNode = _methods.getNext();
		}
		_nInheritedAttributes = getInheritedAttributes();
		_nInheritedMethods = getInheritedMethods();
	}
	/** _numberOfAttributes 에 대한 read access 함수이다.
	 */
	public int numberOfAttributes() {
		return _numberOfAttributes;
	}
	/** _numberOfInvisibleAttributes 에 대한 read access 함수이다.
	 */
	public int numberOfInvisibleAttributes() {
		return _numberOfInvisibleAttributes;
	}
	/** _numberOfMethods 에 대한 read access 함수이다.
	 */
	public int numberOfMethods() {
		return _numberOfMethods;
	}
	/** _numberOfInvisibleMethods 에 대한 read access 함수이다.
	 */
	public int numberOfInvisibleMethods() {
		return _numberOfInvisibleMethods;
	}
	/** 상속된 어트리뷰트랑 이 클래스의 어트리뷰트 수의 합을 구하는 함수이다.
	 */
	public int nInheritanceIncludeAttributes() {
		return _nInheritedAttributes + _numberOfAttributes;
	}
	/** _nInheritedAttributes 에 대한 read access 함수이다.
	 */
	public int nInheritedAttributes() {
		return _nInheritedAttributes;
	}
	/** 상속된 메소드랑 이 클래스의 메소드 수의 합을 구하는 함수이다.
	 */
	public int nInheritanceIncludeMethods() {
		return _nInheritedMethods + _numberOfMethods;
	}
	/** _nInheritedMethods 에 대한 read access 함수이다.
	 */
	public int nInheritedMethods() {
		return _nInheritedMethods;
	}
	/** 상위 클래스의 CBO값까지 누적한 CBO값을 구하는 함수이다.
	 */
	public int nTotalCOB() {
		return _cbo + getSuperCOB();
	}
	/** _cbo 에 대한 read access 함수이다.
	 */
	public int nTotalNonInhetritedCBO() {
		return _cbo;
	}
	/** _noom 에 대한 read access 함수이다.
	 */
	public int nTotalNOOM() {
		return _noom;
	}
	/** (_noam * _nDescendant) 에 값을 구하는 함수이다.
	 */
	public int nTotalPFDenominator() {
		return _numberOfMethods * _nDescendants;
	}
	/** number of descendants 수를 셋팅하는 함수이다.
	 * 원래는 child class 수이지만 Together에서는 descendant 수를 이용하기 때문이다.
	 */
	public void setNOCC(MetricsData data) {
		data.setMetricsData(MetricsData.Inocc,_nDescendants); // 34
	}
	/** 조인 테이블 경우 필드가 어느 테이블에 속하는지 결정하는 함수이다.
	 */
	public void determineAmbiguousFieldNamesForJoinTablesAndCollectSQL(SQLTableInfoList allTableInfos) {
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			mNode.determineAmbiguousFieldNamesForJoinTablesAndCollectSQL(allTableInfos,sqlList);
			mNode = _methods.getNext();
		}
	}
	/** 이 클래스에서 출력된 HTML 파일 이름들을 모으는 함수이다.
	 */
	public void collectHtmlOutputFileList() {
		MethodInfoNode mNode = _methods.getFirst();
		while (mNode != null) {
			if (mNode.htmlFileName.length() > 0) {
				htmlOutputFullFileNameList.insertIgnoreCase(mNode.htmlFileName);
				htmlOutputFileList.insertIgnoreCase(mNode.htmlShortFileName);
			}
			mNode = _methods.getNext();
		}
	}
}

