/*
 * Created on 2004-11-20
 *
 * Part of the Symbian development plug-in for Eclipse
 * Copyright (C) Mikołaj Zalewski, 2004
 */
package pl.zalewski.mikolaj.symbdev.core.internal.mmpmodel;

import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.util.Assert;
import org.eclipse.swt.widgets.Listener;

import pl.zalewski.mikolaj.symbdev.SymbdevPlugin;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.IAIFDeclaration;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.ICompilerOptions;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.ILanguage;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.IMMPModel;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.MMPFileReference;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.MMPModelException;
import pl.zalewski.mikolaj.symbdev.core.mmpmodel.MMPSourceFileReference;

/**
 * A model of information contained in an MMP file. Changes to this
 * model affects only the object's variables. Use FileMMPModel for a
 * model which is kept in sync with a file. 
 * 
 * @author mikolaj
 */
public class MMPModel implements IMMPModel {

    public MMPModel()
    {
        fListeners=new LinkedList[CHANGE_MAX+1];
        for (int i=1; i<=CHANGE_MAX; i++)
            fListeners[i]=new LinkedList();
    }
    
    //======================
    //  Target information
    //======================
    public String getTargetFilename() {
        return fTargetFilename;
    }

    public void setTargetFilename(String name) throws CoreException {
        fTargetFilename=name;
        fireChange(CHANGE_TARGET_NAME);
    }

    public String getTargetPath() {
        return fTargetPath;
    }

    public void setTargetPath(String path) throws CoreException {
        path=path.trim();
        if (!path.endsWith("\\")) fTargetPath=path+'\\';
            else fTargetPath=path;
        fireChange(CHANGE_TARGET_PATH);
    }

    public String getTargetType() {
        return fTargetType;
    }

    public void setTargetType(String type) throws CoreException {
        fTargetType=type;
        fireChange(CHANGE_TARGET_TYPE);
    }
    
    //=================
    //      UIDs
    //=================
    public int getUID2() {
        return fUID2;
    }

    public int getUID3() {
        return fUID3;
    }

    public void setUID2(int uid) throws CoreException {
        fUID2=uid;
        fireChange(CHANGE_UID2);
    }

    public void setUID3(int uid) throws CoreException {
        fUID3=uid;
        fireChange(CHANGE_UID3);
    }
    
    //==============
    //  Languages
    //==============
    public ILanguage[] getLanguages() {
        ILanguage[] ret=new ILanguage[fLangTab.size()];
        Iterator it=fLangTab.iterator();
        int i=0;
        while (it.hasNext())
            ret[i++]=(ILanguage)it.next();
        return ret;
    }

    public void addLanguage(Language l)
    	throws CoreException
    {
        fLangTab.add(l);        
    }
    
    public void addLanguages(String[] codes) throws CoreException
    {
        for (int i=0; i<codes.length; i++)
        {
            String code=codes[i].trim();
            Language l=new Language(code);
            if (fLangTab.contains(l))
                continue;
            addLanguage(l);
        }
        fireChange(CHANGE_LANG);
    }

    public void removeLanguage(int pos) throws CoreException
    {
        Iterator it=fLangTab.iterator();
        for (int i=0; i<=pos; i++)
        {
            if (!it.hasNext())
                throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Language not found");
            it.next();
        }
        it.remove();
        fireChange(CHANGE_LANG);
    }
    
    //===============
    //   Listeners
    //===============
    public void addChangeListener(int type, Listener listener) {
        fListeners[type].add(listener);
    }

    public void removeChangeListener(int type, Listener listener) {
        Iterator it=fListeners[type].iterator();
        while (it.hasNext())
        {
            Listener l=(Listener)it.next();
            if (l==listener)
            {
                it.remove();
                return;
            }
        }
        throw new IllegalArgumentException();
    }
    
    public void fireChange(int type)
    {
        if (fFreezeFireChanges>0)
        {
            fFrozenChanges[type]=true;
            return;
        }
        
        final int fintype=type;
        if (!fListeners[type].isEmpty())
            SymbdevPlugin.getStandardDisplay().asyncExec(
                new Runnable() 
                {
                    public void run()
                    {
                        Iterator it=fListeners[fintype].iterator();
                        while (it.hasNext())
                        {
                            Listener l=(Listener)it.next();
                            try {
                                l.handleEvent(null);
                            }
                            catch (Throwable t)
                            {
                                SymbdevPlugin.getDefault().getLog().log(
                                        SymbdevPlugin.createErrorStatus(0, t.getMessage(),
                                                t));
                            }
                        }
                    }
                });
    }
    
    public void freezeFireChanges()
    {
        if (fAIFDeclaration!=null)
            fAIFDeclaration.freezeFireChanges();
        if (fFreezeFireChanges==0)
        {
            fFrozenChanges=new boolean[CHANGE_MAX+1];
        }
        fFreezeFireChanges++;
    }
    
    public void unfreezeFireChanges()
    {
        fFreezeFireChanges--;
        Assert.isTrue(fFreezeFireChanges>=0);
        if (fFreezeFireChanges==0)
        {
            for (int i=1; i<=CHANGE_MAX; i++)
                if (fFrozenChanges[i])
                    fireChange(i);
        }
        if (fAIFDeclaration!=null)
            fAIFDeclaration.unfreezeFireChanges();
    }
    
    public String[] getMacros() {
        return (String[])fMacros.toArray(new String[fMacros.size()]);
    }

    public void addMacro(String macro) throws CoreException {
        fMacros.add(macro);
        fireChange(CHANGE_MACRO);        
    }
    
    public void setMacro(int pos, String macro) throws CoreException {
        fMacros.set(pos, macro);
        fireChange(CHANGE_MACRO);
    }

    public void removeMacro(int pos) throws CoreException {
        Iterator it;
        try {
            it=fMacros.listIterator(pos);
        }
        catch (IndexOutOfBoundsException e) {
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Macro not found");        
        }
        it.next();
        it.remove();
        fireChange(CHANGE_MACRO);
    }    


    public ICompilerOptions[] getCompilerOptions() {
        return (ICompilerOptions[])fCompilerOptions.toArray(new ICompilerOptions[fCompilerOptions.size()]);
    }

    public void addCompilerOptions(String id, String options) throws CoreException {
        fCompilerOptions.add(new CompilerOptions(id, options));
        fireChange(CHANGE_OPTION);        
    }

    public void setCompilerOptions(int pos, String id, String options) throws CoreException {
        fCompilerOptions.set(pos, new CompilerOptions(id, options));
        fireChange(CHANGE_OPTION);        
    }    
    
    public void removeCompilerOptions(int pos) throws CoreException {
        Iterator it;
        try {
            it=fCompilerOptions.listIterator(pos);
        }
        catch (IndexOutOfBoundsException e) {
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Option not found");        
        }
        it.next();
        it.remove();
        fireChange(CHANGE_OPTION);        
    }    
    
    public int getHeapMin() {
        return fMinHeap;
    }

    public void setHeapMin(int heapMin) throws CoreException {
        fMinHeap=heapMin;
        fireChange(CHANGE_HEAPSIZE);
    }

    public int getHeapMax() {
        return fMaxHeap;
    }

    public void setHeapMax(int heapMax) throws CoreException {
        fMaxHeap=heapMax;
        fireChange(CHANGE_HEAPSIZE);
    }

    public int getStackSize() {
        return fStackSize;
    }

    public void setStackSize(int stackSize) throws CoreException {
        fStackSize=stackSize;
        fireChange(CHANGE_STACKSIZE);
    }

    public String getPriority() {
        return fPriority;
    }

    public void setPriority(String priority) throws CoreException {
        fPriority=priority;
        fireChange(CHANGE_PRIORITY);
    }

    public boolean getNoStrictDef() {
        return fNoStrictDef;
    }
    
    public void setNoStrictDef(boolean noStrictDef) throws CoreException {
        fNoStrictDef = noStrictDef;
        fireChange(CHANGE_NO_STRICT_DEF);
    }
    
    public boolean getSrcDbg() {
        return fSrcDbg;
    }
    
    public void setSrcDbg(boolean srcDbg) throws CoreException {
        fSrcDbg = srcDbg;
        fireChange(CHANGE_SRC_DBG);
    }

    public boolean getStrictDepend() {
        return fStrictDepend;
    }

    public void setStrictDepend(boolean strictDepend) throws CoreException {
        fStrictDepend = strictDepend;
        fireChange(CHANGE_STRICT_DEPEND);
    }
    
    public String getLinkAs() {
        return fLinkAs;
    }

    //===================
    //     Libraries
    //===================
    public String[] getLibraries() {
        return (String[])fLibraryTab.toArray(new String[fLibraryTab.size()]);
    }

    public void addLibrary(String name) throws CoreException {
        String nameUpper=name.toUpperCase();
        if (fLibrarySet.contains(nameUpper))
            throw new MMPModelException(MMPModelException.ITEM_EXISTS,
                    "Library already exists");
        fLibraryTab.add(name);
        fLibrarySet.add(nameUpper);
        fireChange(CHANGE_LIBRARIES);
    }

    public void removeLibrary(int pos) throws CoreException {
        Iterator it=fLibraryTab.iterator();
        String lib=null;
        for (int i=0; i<=pos; i++)
        {
            if (!it.hasNext())
                throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Library not found");
            lib=(String)it.next();
        }
        it.remove();
        fLibrarySet.remove(lib.toUpperCase());
        fireChange(CHANGE_LIBRARIES);
    }

    public String[] getDebugLibraries() {
        return (String[])fDebugLibraryTab.toArray(new String[fDebugLibraryTab.size()]);
    }

    public void addDebugLibrary(String name) throws CoreException {
        String nameUpper=name.toUpperCase();
        if (fDebugLibrarySet.contains(nameUpper))
            throw new MMPModelException(MMPModelException.ITEM_EXISTS,
                    "Library already exists");
        fDebugLibraryTab.add(name);
        fDebugLibrarySet.add(nameUpper);
        fireChange(CHANGE_DEBUG_LIBRARIES);
    }

    public void removeDebugLibrary(int pos) throws CoreException {
        Iterator it=fDebugLibraryTab.iterator();
        String lib=null;
        for (int i=0; i<=pos; i++)
        {
            if (!it.hasNext())
                throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Library not found");
            lib=(String)it.next();
        }
        it.remove();
        fDebugLibrarySet.remove(lib.toUpperCase());
        fireChange(CHANGE_DEBUG_LIBRARIES);
    }

    //================
    //  Include dirs
    //================
    public String[] getSystemIncludes() {
        return (String[])fSystemIncludes.toArray(new String[fSystemIncludes.size()]);
    }
        
    public void addSystemInclude(String name)
    	throws CoreException
    {
        fSystemIncludes.add(name);
        fireChange(CHANGE_SYSTEM_INCLUDES);
    }
    
    public void removeSystemInclude(int pos)
    	throws CoreException
    {
        if (pos>=fSystemIncludes.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Include directory entry not found");            
        fSystemIncludes.remove(pos);
        fireChange(CHANGE_SYSTEM_INCLUDES);
    }

    public void setSystemInclude(int pos, String name)
    	throws CoreException
    {
        if (pos>=fSystemIncludes.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Include directory entry not found");            
        fSystemIncludes.set(pos, name);
        fireChange(CHANGE_SYSTEM_INCLUDES);
    }

    public String[] getUserIncludes() {
        return (String[])fUserIncludes.toArray(new String[fUserIncludes.size()]);
    }
        
    public void addUserInclude(String name)
    	throws CoreException
    {
        fUserIncludes.add(name);
        fireChange(CHANGE_USER_INCLUDES);
    }
    
    public void removeUserInclude(int pos)
    	throws CoreException
    {
        if (pos>=fUserIncludes.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Include directory entry not found");            
        fUserIncludes.remove(pos);
        fireChange(CHANGE_USER_INCLUDES);
    }

    public void setUserInclude(int pos, String name)
    	throws CoreException
    {
        if (pos>=fUserIncludes.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Include directory entry not found");            
        fUserIncludes.set(pos, name);
        fireChange(CHANGE_USER_INCLUDES);
    }
    
    //================
    //  Resources
    //================    
    public MMPFileReference[] getResources()
    {
        return (MMPFileReference[])fResources.toArray(new MMPFileReference[fResources.size()]);
    }
    
    public void addResource(MMPFileReference file) throws CoreException
    {
        fResources.add(file);
        fireChange(CHANGE_RESOURCES);
    }

    /**
     * Note: In MMPModel this implementation allways throws
     * IllealStateException. Use addResource(MMPFileReferenace) to
     * add resources to memory MMP models. This is overwritten in
     * FileMMPModel - that function works.
     * @param file The path to the file to add
     * @throws CoreException If anything goes wrong
     */
    public void addResource(IPath file) throws CoreException
    {
        throw new IllegalStateException();
    }
    
    public void removeResource(int pos) throws CoreException
    {
        if (pos>=fResources.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Resources file entry not found");            
        fResources.remove(pos);
        fireChange(CHANGE_RESOURCES);
    }
    
    public void setResource(int pos, MMPFileReference file) throws CoreException
    {
        if (pos>=fResources.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Resources file entry not found");            
        fResources.set(pos, file);
        fireChange(CHANGE_RESOURCES);
        
    }

    /**
     * Note: In MMPModel this implementation allways throws
     * IllealStateException. Use setResource(int, MMPFileReferenace) to
     * add resources to memory MMP models. This is overwritten in
     * FileMMPModel - that function works.
     * @param pos The position of the resource to set
     * @param file The path to the file to add
     * @throws CoreException If anything goes wrong
     */
    public void setResource(int pos, IPath file) throws CoreException
    {
        throw new IllegalStateException();
    }    

    public MMPFileReference[] getSystemResources()
    {
        return (MMPFileReference[])fSystemResources.toArray(new MMPFileReference[fSystemResources.size()]);
    }
    
    public void addSystemResource(MMPFileReference file) throws CoreException
    {
        fSystemResources.add(file);
        fireChange(CHANGE_SYSTEM_RESOURCES);
    }

    /**
     * Note: In MMPModel this implementation allways throws
     * IllealStateException. Use addSystemResource(MMPFileReferenace)
     * to add resources to memory MMP models. This is overwritten in
     * FileMMPModel - that function works.
     * @param file The path to the file to add
     * @throws CoreException If anything goes wrong
     */
    public void addSystemResource(IPath file) throws CoreException
    {
        throw new IllegalStateException();
    }
    
    public void removeSystemResource(int pos) throws CoreException
    {
        if (pos>=fSystemResources.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Resources file entry not found");            
        fSystemResources.remove(pos);
        fireChange(CHANGE_SYSTEM_RESOURCES);
    }
    
    public void setSystemResource(int pos, MMPFileReference file) throws CoreException
    {
        if (pos>=fSystemResources.size())
            throw new MMPModelException(MMPModelException.ITEM_NOT_FOUND, "Resources file entry not found");            
        fSystemResources.set(pos, file);
        fireChange(CHANGE_SYSTEM_RESOURCES);
        
    }

    /**
     * Note: In MMPModel this implementation allways throws
     * IllealStateException. Use setSystemResource(int, MMPFileReferenace) to
     * add system resources to memory MMP models. This is overwritten in
     * FileMMPModel - that function works.
     * @param pos The position of the resource to set
     * @param file The path to the file to add
     * @throws CoreException If anything goes wrong
     */
    public void setSystemResource(int pos, IPath file) throws CoreException
    {
        throw new IllegalStateException();
    }
    
    //===============
    //     AIFs
    //===============
    protected void linkAIFDeclaration() {
        fAIFListener=new Listener() {
            public void handleEvent(org.eclipse.swt.widgets.Event e) {
                AIFDeclarationChanged();
            }
        };
        fAIFDeclaration.addChangeListener(fAIFListener);
        fAIFDeclaration.fConnected=true;
        for (int i=0; i<fFreezeFireChanges; i++)
            fAIFDeclaration.freezeFireChanges();
    }
    
    protected void unlinkAIFDeclaration() {
        fAIFDeclaration.removeChangeListener(fAIFListener);
        for (int i=0; i<fFreezeFireChanges; i++)
            fAIFDeclaration.unfreezeFireChanges();
        fAIFDeclaration.fConnected=false;
        fAIFDeclaration=null;
        fAIFListener=null;
    }
    
    public IAIFDeclaration getAIFDeclaration() {
        return fAIFDeclaration;
    }
    
    public void createAIFDeclaration(IPath ref, String targetname) throws CoreException {
        throw new IllegalStateException();
    }
    
    public void createAIFDeclaration(MMPFileReference ref, String targetname) throws CoreException {
        fAIFDeclaration=new AIFDeclaration();
        fAIFDeclaration.fSourceFile=ref;
        fAIFDeclaration.fTargetFilename=targetname;
        linkAIFDeclaration();
        fireChange(CHANGE_AIF);
    }
    
    public void removeAIFDeclaration() throws CoreException {
        unlinkAIFDeclaration();
        fireChange(CHANGE_AIF);
    }
    
    public void AIFDeclarationChanged()
    {
        fireChange(CHANGE_AIF);
    }
    
    //=============
    //   SOURCES
    //=============
    public void addSourceMMPItem(MMPFileReference file) {
        fSourceMMPItems.add(file);
        fireChange(CHANGE_SOURCE);
    }
    
    public MMPSourceFileReference[] getSourceFiles() {
        MMPSourceFileReference[] ret=new MMPSourceFileReference[fSourceMMPItems.size()];
        Iterator it=fSourceMMPItems.iterator();
        for (int i=0; i<ret.length; i++) {
            MMPFileReference file=(MMPFileReference)it.next();
            ret[i]=new MMPSourceFileReference(file, MMPSourceFileReference.ITEM_SOURCE_USER);
        }
        return ret;
    }
    
    public IPath[] getSourceDirs() throws CoreException {
        return new IPath[0];
    }
    
    public IPath[] getAllSourceAutoDirs() throws CoreException {
        return new IPath[0];
    }
    
    public void removeSourceDir(IPath path) throws CoreException {
    }
    
    /**
     * throws IllegalStateException, like addResource
     * @see addResource(IPath)
     */
    public void addSourceFile(IPath file) throws CoreException {
        throw new IllegalStateException();
    }
    
    /**
     * throws IllegalStateException, like addResource
     * @see addResource(IPath)
     */
    public void addSourceDir(IPath dir) throws CoreException {
        throw new IllegalStateException();
    }
    
    /**
     * throws IllegalStateException, like addResource
     * @see addResource(IPath)
     */
    public void removeSourceFile(IPath file, IPath rootPath) throws CoreException {
        throw new IllegalStateException();
    }
    
    public void removeSourceFile(int pos) throws CoreException {
        fSourceMMPItems.remove(pos);
        fireChange(CHANGE_SOURCE);
    }
        
    //================
    //  Miscellanous
    //================
    public void setLinkAs(String linkAs) throws CoreException {
        if (linkAs==null)
            linkAs="";
        linkAs=linkAs.trim();
        if (linkAs.indexOf(' ')!=-1)
            throw new MMPModelException(MMPModelException.INVALID_ARGUMENT,
                    "LINKAS field cannot contain spaces");
        fLinkAs=linkAs;
        fireChange(CHANGE_LINK_AS);
    }    

        
    public static boolean isHashListsEqual(LinkedHashSet l1, LinkedHashSet l2)
    {
        if (l1==l2)
            return true;
        if (l1==null || l2==null) // l1==null && l2==null is covered by the previous case
            return false;
        
        Iterator i1=l1.iterator();
        Iterator i2=l2.iterator();
        while (i1.hasNext())
        {
            if (!i2.hasNext())
                return false;
            if (!i1.next().equals(i2.next()))
                return false;
        }
        if (i2.hasNext())
            return false;
        return true;
    }
    
    public static boolean isEqual(Object o1, Object o2)
    {
        if (o1==o2)
            return true;
        
        return (o1!=null && o1.equals(o2)); // if o1==null then o2!=null and the result is false
    }
    
    /**
     * Loads the data from the model m to this model. The data is
     * loaded directly into variables, not through set* methods.
     * A use of the parser in this method would lead to a deadlock
     * @param m The model to load data from
     */
    public void loadModel(MMPModel m)
    {
        freezeFireChanges();
        
        try {
	        if (!isEqual(fTargetFilename, m.fTargetFilename))
	        {
	            fTargetFilename=m.fTargetFilename;
	            fireChange(CHANGE_TARGET_NAME);
	        }
	        if (!isEqual(fTargetPath, m.fTargetPath))
	        {
	            fTargetPath=m.fTargetPath;
	            fireChange(CHANGE_TARGET_PATH);
	        }
	        if (!isEqual(fTargetType, m.fTargetType))
	        {
	            fTargetType=m.fTargetType;
	            fireChange(CHANGE_TARGET_TYPE);
	        }
	        if (fUID2!=m.fUID2)
	        {
	            fUID2=m.fUID2;
	            fireChange(CHANGE_UID2);
	        }
	        if (fUID3!=m.fUID3)
	        {
	            fUID3=m.fUID3;
	            fireChange(CHANGE_UID3);
	        }
	        if (!isHashListsEqual(fLangTab, m.fLangTab))
	        {
	            fLangTab=new LinkedHashSet(m.fLangTab);
	            fireChange(CHANGE_LANG);
	        }
	        
	        if (!isEqual(fMacros, m.fMacros))
	        {
	            fMacros=new LinkedList(m.fMacros);
	            fireChange(CHANGE_MACRO);
	        }
	        if (!isEqual(fCompilerOptions, m.fCompilerOptions))
	        {
	            fCompilerOptions=new LinkedList(m.fCompilerOptions);
	            fireChange(CHANGE_OPTION);
	        }
	        
	        if (fMinHeap!=m.fMinHeap)
	        {
	            fMinHeap=m.fMinHeap;
	            fireChange(CHANGE_HEAPSIZE);
	        }
	        if (fMaxHeap!=m.fMaxHeap)
	        {
	            fMaxHeap=m.fMaxHeap;
	            fireChange(CHANGE_HEAPSIZE);
	        }
	        if (fStackSize!=m.fStackSize)
	        {
	            fStackSize=m.fStackSize;
	            fireChange(CHANGE_STACKSIZE);
	        }
	        if (!isEqual(fPriority, m.fPriority))
	        {
	            fPriority=m.fPriority;
	            fireChange(CHANGE_PRIORITY);
	        }
	        if (fStrictDepend!=m.fStrictDepend)
	        {
	            fStrictDepend=m.fStrictDepend;
	            fireChange(CHANGE_STRICT_DEPEND);
	        }
	        if (fNoStrictDef!=m.fNoStrictDef)
	        {
	            fNoStrictDef=m.fNoStrictDef;
	            fireChange(CHANGE_NO_STRICT_DEF);
	        }
	        if (fSrcDbg!=m.fSrcDbg)
	        {
	            fSrcDbg=m.fSrcDbg;
	            fireChange(CHANGE_SRC_DBG);
	        }
	        if (!isEqual(fLinkAs, m.fLinkAs))
	        {
	            fLinkAs=m.fLinkAs;
	            fireChange(CHANGE_LINK_AS);
	        }
	        if (!isEqual(fLibraryTab, m.fLibraryTab))
	        {
	            fLibraryTab=new LinkedList(m.fLibraryTab);
	            fLibrarySet=new HashSet(m.fLibrarySet);
	            fireChange(CHANGE_LIBRARIES);
	        }
	        if (!isEqual(fDebugLibraryTab, m.fDebugLibraryTab))
	        {
	            fDebugLibraryTab=new LinkedList(m.fDebugLibraryTab);
	            fDebugLibrarySet=new HashSet(m.fDebugLibrarySet);
	            fireChange(CHANGE_DEBUG_LIBRARIES);
	        }
	        if (!isEqual(fUserIncludes, m.fUserIncludes))
	        {
	            fUserIncludes=new LinkedList(m.fUserIncludes);
	            fireChange(CHANGE_USER_INCLUDES);
	        }
	        if (!isEqual(fSystemIncludes, m.fSystemIncludes))
	        {
	            fSystemIncludes=new LinkedList(m.fSystemIncludes);
	            fireChange(CHANGE_SYSTEM_INCLUDES);
	        }
	        if (!isEqual(fResources, m.fResources))
	        {
	            fResources=new LinkedList(m.fResources);
	            fireChange(CHANGE_RESOURCES);
	        }
	        if (!isEqual(fSystemResources, m.fSystemResources))
	        {
	            fSystemResources=new LinkedList(m.fSystemResources);
	            fireChange(CHANGE_SYSTEM_RESOURCES);
	        }
	        if (!isEqual(fSourceMMPItems, m.fSourceMMPItems))
	        {
	            fSourceMMPItems=new LinkedList(m.fSourceMMPItems);
	            fireChange(CHANGE_SOURCE);	            
	        }
	        if (fAIFDeclaration!=null && m.fAIFDeclaration==null)
	        {
	            unlinkAIFDeclaration();
	            fireChange(CHANGE_AIF);
	        } else if (m.fAIFDeclaration!=null)
	        {
	            boolean changed=false;
	            if (fAIFDeclaration==null)
	            {
	                fAIFDeclaration=new AIFDeclaration();
	                linkAIFDeclaration();
	                changed=true;
	            }
	            if (fAIFDeclaration.copyFrom(m.fAIFDeclaration) || changed)
	                fireChange(CHANGE_AIF);
	        } else if (fAIFDeclaration!=null)
	        {
	            Assert.isTrue(m.fAIFDeclaration==null);
	            unlinkAIFDeclaration();
	            fireChange(CHANGE_AIF);
	        }
        }
        finally
        {
            unfreezeFireChanges();
        }
    }
    
    protected String fTargetFilename="";
    protected String fTargetPath="";
    protected String fTargetType="";
    protected int fUID2, fUID3;
    protected LinkedHashSet fLangTab=new LinkedHashSet();
    protected LinkedList fMacros=new LinkedList();
    protected LinkedList fCompilerOptions=new LinkedList();
    protected LinkedList fLibraryTab=new LinkedList();
    protected HashSet fLibrarySet=new HashSet();
    protected LinkedList fDebugLibraryTab=new LinkedList();
    protected HashSet fDebugLibrarySet=new HashSet();
    protected LinkedList fUserIncludes=new LinkedList();
    protected LinkedList fSystemIncludes=new LinkedList();
    protected LinkedList fResources=new LinkedList();
    protected LinkedList fSystemResources=new LinkedList();
    protected int fMinHeap, fMaxHeap, fStackSize;
    protected String fPriority="";
    protected boolean fSrcDbg, fNoStrictDef, fStrictDepend;
    protected String fLinkAs="";
    protected AIFDeclaration fAIFDeclaration;
    protected LinkedList fSourceMMPItems=new LinkedList();
    /**
     * Used to pass the changed signals from the AIF object to this
     * object
     */
    protected Listener fAIFListener;
    
    protected LinkedList[] fListeners;
    protected int fFreezeFireChanges;
    protected boolean[] fFrozenChanges;
}
