/*-------------------------------------------------------------------------------
Copyright (c)2000 TogetherSoft Corporation. Patents pending. All rights reserved.
-------------------------------------------------------------------------------*/

package com.togethersoft.modules.patterns.UNIVERSAL.CLASS.GoF.Abstract_Factory;

import java.util.Enumeration;
import java.util.Vector;

import com.togethersoft.openapi.ide.message.IdeMessageManagerAccess;
import com.togethersoft.openapi.ide.message.IdeMessageType;

import com.togethersoft.openapi.ide.inspector.IdeInspectorComponent;
import com.togethersoft.openapi.ide.inspector.IdeInspectorPropertySetComponent;
import com.togethersoft.openapi.ide.inspector.IdeInspectorProperty;

import com.togethersoft.openapi.sci.SciClass;
import com.togethersoft.openapi.sci.SciProperty;
import com.togethersoft.openapi.sci.SciOperation;
import com.togethersoft.openapi.sci.SciUtil;
import com.togethersoft.openapi.sci.SciGenericFactoryException;

import com.togethersoft.openapi.sci.pattern.SciPattern;
import com.togethersoft.openapi.sci.pattern.SciPatternUtil;

import com.togethersoft.openapi.util.propertyMap.Property;
import com.togethersoft.openapi.util.propertyMap.PropertyMapListener;
import com.togethersoft.openapi.util.propertyMap.PropertyMapEvent;
import com.togethersoft.openapi.util.propertyMap.PropertyMapAdapter;

import com.togethersoft.modules.patterns.UNIVERSAL.CLASS.ClassPatternBase;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternBaseException;
import com.togethersoft.modules.patterns.UNIVERSAL.PatternUIBuilder;

/**
 * GoF Abstract Factory pattern.
 * @author TogetherSoft
 */
public class Pattern extends ClassPatternBase implements SciPattern {

    private static final String PATTERN_NAME = "Abstract Factory";

    public Pattern() {
        setPatternDisplayName(PATTERN_NAME);

        myConcreteFactoryNumber = 1;
        myAbstractProductNumber = 1;

        setPropertyValue(SELECTED_CLASS_ROLE, null);
        setPropertyValue(ABSTRACT_FACTORY, "AbstractFactory");
        addPropertyValue(ABSTRACT_PRODUCT, "AbstractProduct");
        addPropertyValue(CONCRETE_FACTORY, "ConcreteFactory");
        setPropertyValue(getConcreteProductPropertyKey(myConcreteFactoryNumber,myAbstractProductNumber), "ConcreteProduct");
        setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
        setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);
    }

    protected void finalize() throws Throwable {
        super.finalize();
        if (myUIBuilder != null) {
            myUIBuilder.removeAllListenedInspectorProperties();
            myUIBuilder = null;
        }
        myClassRoleVector.removeAllElements();
        myClassRoleVector = null;
        myProductPropertyKey = null;
        myPagePropertyKey = null;
    }

    public boolean prepare() {
        try {
            super.prepare();
            notImplementedForIDL();

            removeTable();

            myInsideListener = true;

            myConcreteFactoryNumber = 1;
            myAbstractProductNumber = 1;

            setPropertyValue(SELECTED_CLASS_ROLE, null);
            setPropertyValue(ABSTRACT_FACTORY, "AbstractFactory");
            addPropertyValue(ABSTRACT_PRODUCT, "AbstractProduct");
            addPropertyValue(CONCRETE_FACTORY, "ConcreteFactory");
            addPropertyValue(getConcreteProductPropertyKey(myConcreteFactoryNumber,myAbstractProductNumber), "ConcreteProduct");
            setPropertyValue(EXPORT_DOCUMENTATION, Boolean.FALSE);
            setPropertyValue(CREATE_PATTERN_LINKS, Boolean.TRUE);

            myClassRoleVector.removeAllElements();

            if (getSelectedClass() == null) {
            }
            else {
                SciClass selectedClass = getSelectedClass();

                // Fill class roles
                myClassRoleVector.addElement(ABSTRACT_FACTORY);
                myClassRoleVector.addElement(ABSTRACT_PRODUCT);
                if (!selectedClass.hasProperty(SciProperty.INTERFACE)) {
                    myClassRoleVector.addElement(CONCRETE_FACTORY);
                }
                myClassRoleVector.addElement(CONCRETE_PRODUCT);

                String currentRole = null;
                if (SciPatternUtil.findExtendedPatternLinkClient(selectedClass, PATTERN_NAME, CONCRETE_FACTORY) != null) {
                    currentRole = CONCRETE_FACTORY;
                }
                else if (SciPatternUtil.findExtendedPatternLinkClient(selectedClass, PATTERN_NAME, CONCRETE_PRODUCT) != null) {
                    currentRole = CONCRETE_PRODUCT;
                }
                else if (SciPatternUtil.findPatternLinkSupplier(selectedClass, PATTERN_NAME, FACTORY) != null) {
                    currentRole = ABSTRACT_PRODUCT;
                }
                else {
                    currentRole = ABSTRACT_FACTORY;
                }
                setPropertyValue(SELECTED_CLASS_ROLE, currentRole);
            }

            myInsideListener = false;

            if (!initializeInspector()) {
                return false;
            }
            assignActivityResponse();
            return true;
        }
        catch(PatternBaseException e) {
            return false;
        }
    }

    public boolean canApply() {
        try {
            String abstractFactoryName = classNameShouldBeCorrect(ABSTRACT_FACTORY);
            SciClass abstractFactoryClass = findClassByName(abstractFactoryName);
            shouldBeWritable(ABSTRACT_FACTORY, abstractFactoryClass);

            // Check for Abstract Products
            boolean atLeastOneAbstractProductIsDefined = false;
            Enumeration abstractProducts = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (abstractProducts.hasMoreElements()) {
                String abstractProductName = classNameShouldBeCorrect((Property)abstractProducts.nextElement());
                //SciClass abstractProductClass = findClassByName(abstractProductName);
                atLeastOneAbstractProductIsDefined |= abstractProductName != null && ! EMPTY_STRING.equals(abstractProductName);
            }
            if (!atLeastOneAbstractProductIsDefined) {
                throw new PatternBaseException(ONE_ABSTRACT_PRODUCT_SHOULD_BE_DEFINED);
            }

            // Check for Concrete Factories
            boolean atLeastOneConcreteFactoryIsDefined = false;
            int factoryNum = 0;
            Enumeration concreteFactories = propertiesEnumeration(CONCRETE_FACTORY);
            while (concreteFactories.hasMoreElements()) {
                factoryNum++;
                String concreteFactoryName = classNameShouldBeCorrect((Property)concreteFactories.nextElement());
                if (concreteFactoryName == null || EMPTY_STRING.equals(concreteFactoryName)) {
                    continue;
                }

                SciClass concreteFactoryClass = findClassByName(concreteFactoryName);
                cannotBeAnInterface(CONCRETE_FACTORY, concreteFactoryClass);
                shouldBeWritable(CONCRETE_FACTORY, concreteFactoryClass);
                atLeastOneConcreteFactoryIsDefined = true;

                int productNum = 0;
                abstractProducts = propertiesEnumeration(ABSTRACT_PRODUCT);
                while (abstractProducts.hasMoreElements()) {
                    productNum++;
                    String abstractProductName = classNameShouldBeCorrect((Property)abstractProducts.nextElement());
                    if (abstractProductName == null || abstractProductName.length() == 0) {
                        continue;
                    }

                    String concreteProductName = getStringPropertyValue(getConcreteProductPropertyKey(factoryNum, productNum));
                    if (concreteProductName == null || concreteProductName.length() == 0) {
                        Object[] arguments = { abstractProductName, concreteFactoryName };
                        String message = createTextByPattern(PATTERN_PRODUCT_NOT_DEFINED, arguments);
                        throw new PatternBaseException(message);
                    }

                    SciClass concreteProductClass = findClassByName(concreteProductName);
                    shouldBeWritable(CONCRETE_PRODUCT, concreteProductClass);
                }
            }

            if (!atLeastOneConcreteFactoryIsDefined) {
                throw new PatternBaseException(ONE_CONCRETE_FACTORY_SHOULD_BE_DEFINED);
            }

            return true;
        }
        catch(PatternBaseException e) {
            return false;
        }
    }

    public void apply() {

        if (!canApply()) {
            return;
        }
        super.apply();

        myClassRoleVector.removeAllElements();
        myUIBuilder.removeAllListenedInspectorProperties();
        myUIBuilder = null;

        String abstractProductName = null;
        String abstractFactoryName = null;
        String concreteFactoryName = null;
        String concreteProductName = null;
        String abstractOperationName = null;

        SciClass abstractProductClass = null;
        SciClass abstractFactoryClass = null;
        SciClass concreteFactoryClass = null;
        SciClass concreteProductClass = null;

        abstractFactoryName = getStringPropertyValue(ABSTRACT_FACTORY);
        abstractFactoryClass = getClassByName(abstractFactoryName, true);
        abstractFactoryClass.setProperty(SciProperty.ABSTRACT, true);

        SciOperation createProductOperation = getFactory().newOperation();
        createProductOperation.setProperty(SciProperty.ABSTRACT, true);

        Enumeration abstractProducts = propertiesEnumeration(ABSTRACT_PRODUCT);
        while (abstractProducts.hasMoreElements()) {
            abstractProductName = (String) ((Property)abstractProducts.nextElement()).getValue();
            if (EMPTY_STRING.equals(abstractProductName)) {
                continue;
            }

            abstractProductClass = getClassByName(abstractProductName, true);

            abstractOperationName = PREFIX_CREATE + abstractProductName.substring(abstractProductName.lastIndexOf(".") + 1);
            createProductOperation.setName(abstractOperationName);

            if (SciUtil.findMemberBySignature(abstractFactoryClass, createProductOperation) == null) {
                SciOperation abstractOperation = (SciOperation) abstractFactoryClass.paste(createProductOperation, null, false);
                abstractOperation.setProperty(SciProperty.ABSTRACT, true);
                abstractOperation.getReturnType().setText(abstractProductName);
                abstractOperation.setReturnType( getLanguageHelper().makePointerType(abstractOperation.getReturnType()) );
            }

            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(abstractFactoryClass, abstractProductClass, PATTERN_NAME, FACTORY, PRODUCT, false);
                SciPatternUtil.addPatternLink(abstractProductClass, abstractFactoryClass, PATTERN_NAME, null, FACTORY, true);
            }
        }

        int factoryNum = 0;
        Enumeration concreteFactories = propertiesEnumeration(CONCRETE_FACTORY);
        while (concreteFactories.hasMoreElements()) {
            factoryNum++;
            concreteFactoryName = (String)((Property)concreteFactories.nextElement()).getValue();
            if (EMPTY_STRING.equals(concreteFactoryName)) {
                break;
            }

            concreteFactoryClass = getClassByName(concreteFactoryName, false);
            if (createPatternLinks()) {
                SciPatternUtil.addPatternLink(abstractFactoryClass, concreteFactoryClass, PATTERN_NAME, null, CONCRETE_FACTORY, false);
            }

            SciOperation concreteCreateProductOperation = null;
            int productNum = 0;
            abstractProducts = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (abstractProducts.hasMoreElements()) {
                productNum++;
                abstractProductName = (String) ((Property)abstractProducts.nextElement()).getValue();
                if (EMPTY_STRING.equals(abstractProductName)) {
                    continue;
                }

                concreteProductName = getStringPropertyValue(getConcreteProductPropertyKey(factoryNum, productNum));
                abstractProductClass = getClassByName(abstractProductName, true);
                concreteProductClass = getClassByName(concreteProductName, false);
                SciPatternUtil.makeStubImplementation(concreteProductClass, abstractProductClass, false, exportDocumentation());

                if (createPatternLinks()) {
                    SciPatternUtil.addPatternLink(abstractProductClass, concreteProductClass, PATTERN_NAME, null, CONCRETE_PRODUCT, false);
                    SciPatternUtil.addPatternLink(concreteFactoryClass, concreteProductClass, PATTERN_NAME, null, PRODUCT, false);
                }

                createProductOperation.setName(PREFIX_CREATE + abstractProductName.substring(abstractProductName.lastIndexOf(".") + 1));
                createProductOperation.getReturnType().setText(abstractProductName);
                createProductOperation.setReturnType( getLanguageHelper().makePointerType(createProductOperation.getReturnType()) );

                if (SciUtil.findMemberBySignature(concreteFactoryClass, createProductOperation) != null) {
                    continue;
                }

                concreteCreateProductOperation = (SciOperation) concreteFactoryClass.paste(createProductOperation, null, false);
                concreteCreateProductOperation.setProperty(SciProperty.ABSTRACT, false);

                try {
                    Object[] arguments = { concreteProductClass.getName(), EMPTY_STRING };
                    String code = createTextByPattern(PATTERN_RETURN_NEW, arguments);
                    concreteCreateProductOperation.setBody(getGenericFactory().newCodeBlock(code));
                }
                catch(SciGenericFactoryException e) {
                    IdeMessageManagerAccess.printMessage(IdeMessageType.ERROR, CODE_GENERATION_ERROR);
                }
            }

            SciPatternUtil.makeStubImplementation(concreteFactoryClass, abstractFactoryClass, false, exportDocumentation());
        }

        myPagePropertyKey = null;
        myProductPropertyKey = null;
    }

/***********************************************/

    private String getConcreteProductPropertyKey(int concreteFactoryNum, int abstractProductNum) {
        String productPropertyKey =  CONCRETE_FACTORY + concreteFactoryNum + ABSTRACT_PRODUCT + abstractProductNum;
        return productPropertyKey;
	}

    private String getConcreteFactoryPagePropertyKey(int concreteFactoryNum) {
        String pagePropertyKey = PAGE_ROOT + CONCRETE_FACTORY + concreteFactoryNum;
        return pagePropertyKey;
	}

    private int getProductNumberByProperty(Property property) {
        int number = 1;
        Enumeration properties = propertiesEnumeration(ABSTRACT_PRODUCT);
        while (properties.hasMoreElements() && property != properties.nextElement()) {
            number++;
        }
        return number;
    }

    private int getPageNumberByProperty(Property property) {
        int number = 1;
        Enumeration properties = propertiesEnumeration(CONCRETE_FACTORY);
        while (properties.hasMoreElements() && property != properties.nextElement()) {
            number++;
        }
        return number;
    }

    private void removeConcreteFactoriesAndProducts() {
        for (int i = 1; i <= myConcreteFactoryNumber; i++) {
            setPropertyValue(getConcreteFactoryPagePropertyKey(i), null);
            for (int j = 1; j <= myAbstractProductNumber; j++) {
                setPropertyValue(getConcreteProductPropertyKey(i, j), null);
            }
        }
        myConcreteFactoryNumber = 0;
        myAbstractProductNumber = 0;
    }

    private void removeTable() {
        myInsideListener = true;

        removeConcreteFactoriesAndProducts();

        setPropertyValue(ABSTRACT_PRODUCT, null);
        setPropertyValue(CONCRETE_FACTORY, null);

        myInsideListener = false;
    }

    private void setProductVisibility(int productNumber, String state) {
        for (int i = 1; i <= myConcreteFactoryNumber; i++) {
            setPropertyValue(getConcreteFactoryPagePropertyKey(i) + SUBNODE_ITEM +
                getConcreteProductPropertyKey(i, productNumber) + PROPERTY_CONDITION, state);
        }
    }

    private void setProductName(int productNumber, String name) {
        for (int i = 1; i <= myConcreteFactoryNumber; i++) {
            setPropertyValue(getConcreteFactoryPagePropertyKey(i) + SUBNODE_ITEM +
                getConcreteProductPropertyKey(i, productNumber) + PROPERTY_NAME, name);
        }
    }

    private void setPageVisibility(int pageNum, String state) {
        String propertyKey = getConcreteFactoryPagePropertyKey(pageNum) + PROPERTY_CONDITION;
        setPropertyValue(propertyKey, state);
    }

    private void setPageName(int pageNum, String name) {
        String propertyKey = getConcreteFactoryPagePropertyKey(pageNum) + PROPERTY_NAME;
        setPropertyValue(propertyKey, name);
    }

    private void removeAllPropertiesFromPage(IdeInspectorPropertySetComponent page) {
        if (page == null) {
            return;
        }
        IdeInspectorProperty[] properties = page.getProperties();
        for (int i = 0; i < properties.length; i++) {
            page.removeProperty(properties[i]);
        }
    }

    private void emptyAllConcreteFactoryPages() {
        Vector factoryPageVector = getConcreteFactoryPages();
        for (int i=0; i < factoryPageVector.size(); i++) {
            removeAllPropertiesFromPage((IdeInspectorPropertySetComponent) factoryPageVector.elementAt(i));
        }
    }

    private void removeAllConcreteFactoryPages() {
        Vector factoryPageVector = getConcreteFactoryPages();
        for (int i=0; i < factoryPageVector.size(); i++) {
            getIdeInspector().removeComponent((IdeInspectorComponent)factoryPageVector.elementAt(i));
        }
    }

    private Vector getConcreteFactoryPages() {
        IdeInspectorComponent [] pages = getIdeInspector().getComponents();
        Vector factoryPageVector = new Vector(pages.length);
        for (int i=0; i < pages.length; i++) {
            if (!FIRST_PAGE_NAME.equals(pages[i].getName())) {
                factoryPageVector.addElement(pages[i]);
            }
        }
        return factoryPageVector;
    }

    private void addNewProductToAllPages(Property property) {
        if (myUIBuilder == null) {
            return;
        }

        try {
            myAbstractProductNumber++;

            String productPropertyKey = null;
            String pageName = null;
            IdeInspectorComponent page = null;
            String productName = (String) property.getValue();
            int pageNum;
            boolean allowInterface = false;

            printFactoriesAndProducts();

            Vector factoryPageVector = getConcreteFactoryPages();
            for (int i = 0; i < factoryPageVector.size(); i++) {
                pageNum = i + 1;
                productPropertyKey = getConcreteProductPropertyKey(pageNum, myAbstractProductNumber);
                page = (IdeInspectorComponent)factoryPageVector.elementAt(i);
                pageName = page.getName();

                setPropertyValue(productPropertyKey, EMPTY_STRING);

                if (myUIBuilder.addClassPropertyToPage(pageName, productPropertyKey, productName,
                    PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                    throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
                }
            }
            factoryPageVector = null;

            getIdeInspector().update();
        }
        catch (PatternBaseException ex) {
            return;
        }
    }

    private void addNewFactoryPage(Property property) {
        if (myUIBuilder == null) {
            return;
        }

        try {
            myConcreteFactoryNumber++;
            // Create new page
            String pageName = getConcreteFactoryPagePropertyKey(myConcreteFactoryNumber);
            String pageDisplayName = PAGE_TITLE_PREFIX + (String)property.getValue();
            if (myUIBuilder.addInspectorPage(pageName, pageDisplayName,
                PatternUIBuilder.UIPresentation.Table) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            /*
            int productNum = 0;
            boolean allowInterface = true;
            String productPropertyKey = null;
            String productName = null;
            Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (products.hasMoreElements()) {
                productNum++;
                productName = (String)((Property)products.nextElement()).getValue();
                if (productName == null || productName.length() == 0) {
                    continue;
                }

                // Add all abstract product properties to page
                productPropertyKey = getConcreteProductPropertyKey(myConcreteFactoryNumber, productNum);
                if (myUIBuilder.addClassPropertyToPage(pageName, productPropertyKey, productName,
                    PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                    throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
                }
            }
            */
            /*
            Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (products.hasMoreElements()) {
                addNewProductToAllPages((Property)products.nextElement());
            }
            */
        }
        catch (PatternBaseException ex) {
            return;
        }
    }

/***********************************************/

    protected boolean initializeInspector() {
        try {
            myUIBuilder = null;
            myUIBuilder = createUIBuilder(true);

            Vector inspectorPropertiesVector = new Vector();

            // Create first page
            String page1DisplayName = "Pattern properties";
            if (myUIBuilder.addInspectorPage(FIRST_PAGE_NAME, page1DisplayName,
                PatternUIBuilder.UIPresentation.Table) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }

            // Add SELECTED_CLASS_ROLE property
            if (getStringPropertyValue(SELECTED_CLASS_ROLE) != null) {
                if (myUIBuilder.addPropertyToPage(FIRST_PAGE_NAME, SELECTED_CLASS_ROLE, SELECTED_CLASS_ROLE,
                    PatternUIBuilder.PropertyType.StringChooser, myClassRoleVector.toArray()) == null) {
                    throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
                }
                inspectorPropertiesVector.addElement(SELECTED_CLASS_ROLE);
            }

            // Add ABSTRACT_FACTORY property
            boolean allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(FIRST_PAGE_NAME, ABSTRACT_FACTORY, ABSTRACT_FACTORY,
                PatternUIBuilder.PropertyType.SingleValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(ABSTRACT_FACTORY);

            // Add ABSTRACT_PRODUCT property
            allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(FIRST_PAGE_NAME, ABSTRACT_PRODUCT, ABSTRACT_PRODUCT,
                PatternUIBuilder.PropertyType.MultiValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(ABSTRACT_PRODUCT);

            // Add CONCRETE_FACTORY property
            allowInterface = true;
            if (myUIBuilder.addClassPropertyToPage(FIRST_PAGE_NAME, CONCRETE_FACTORY, CONCRETE_FACTORY,
                PatternUIBuilder.PropertyType.MultiValue, allowInterface) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CONCRETE_FACTORY);

            // Add EXPORT_DOCUMENTATION property
            if (myUIBuilder.AddExportDocumentationProperty(FIRST_PAGE_NAME) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(EXPORT_DOCUMENTATION);

            // Add "Create pattern links" property
            if (myUIBuilder.AddCreatePatternLinksProperty(FIRST_PAGE_NAME) == null) {
                throw new PatternBaseException(CANNOT_CREATE_PATTERN_UI);
            }
            inspectorPropertiesVector.addElement(CREATE_PATTERN_LINKS);

            removeAllConcreteFactoryPages();
            removeConcreteFactoriesAndProducts();
            // Create pages
            Enumeration factories = propertiesEnumeration(CONCRETE_FACTORY);
            while (factories.hasMoreElements()) {
                addNewFactoryPage((Property) factories.nextElement());
            }

            // Fill pages with properties
            Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (products.hasMoreElements()) {
                addNewProductToAllPages((Property)products.nextElement());
            }

            myUIBuilder.setCreatedPatternUI();
            myUIBuilder.assignListenedInspectorProperties(inspectorPropertiesVector);
            return true;
        }
        catch (PatternBaseException ex) {
            myUIBuilder = null;
            getIdeInspector().clear();
            return false;
       }
    }

    protected void assignActivityResponse() {
        // ABSTRACT_PRODUCT handler
        addPropertyMapListener(ABSTRACT_PRODUCT, new PropertyMapAdapter() {
            public void propertiesChanged(PropertyMapEvent event) {
                if (myInsideListener) {
                    return;
                }
                myInsideListener = true;

                printFactoriesAndProducts();

                emptyAllConcreteFactoryPages();
                // Refill each page
                Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
                while (products.hasMoreElements()) {
                    addNewProductToAllPages((Property)products.nextElement());
                }

                myInsideListener = false;
            }
        });

        addPropertyMapListener(CONCRETE_FACTORY, new PropertyMapAdapter() {
            // CONCRETE_FACTORY handler
            public void propertiesChanged(PropertyMapEvent event) {
                if (myInsideListener) {
                    return;
                }
                myInsideListener = true;

                removeAllConcreteFactoryPages();
                removeConcreteFactoriesAndProducts();

                // Create pages
                Enumeration factories = propertiesEnumeration(CONCRETE_FACTORY);
                while (factories.hasMoreElements()) {
                    addNewFactoryPage((Property) factories.nextElement());
                }

                // Fill pages with properties
                Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
                while (products.hasMoreElements()) {
                    addNewProductToAllPages((Property)products.nextElement());
                }

                myInsideListener = false;
            }
        });

        // Add listener for SELECTED_CLASS_ROLE item
        addPropertyMapListener(SELECTED_CLASS_ROLE, new PropertyMapListener() {
            public void propertiesChanged(PropertyMapEvent event) {
                String role = getStringPropertyValue(SELECTED_CLASS_ROLE);
                SciClass abstractFactoryClass = null;

                if (role != null) {
                    if (role.equals(ABSTRACT_FACTORY)) {
                        abstractFactoryClass = getSelectedClass();
                    }
                    else if (role.equals(ABSTRACT_PRODUCT)) {
                        abstractFactoryClass = SciPatternUtil.findPatternLinkSupplier(getSelectedClass(), PATTERN_NAME, FACTORY);
                        if (abstractFactoryClass == null) {
                            removeTable();
                            addPropertyValue(ABSTRACT_PRODUCT, getSelectedClass().getQualifiedName());
                        }
                    }
                    else if (role.equals(CONCRETE_FACTORY)) {
                        abstractFactoryClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_FACTORY);
                        if (abstractFactoryClass == null) {
                            removeTable();
                            addPropertyValue(CONCRETE_FACTORY, getSelectedClass().getQualifiedName());
                        }
                    }
                    else if (role.equals(CONCRETE_PRODUCT)) {
                        SciClass abstractProductClass = SciPatternUtil.findExtendedPatternLinkClient(getSelectedClass(), PATTERN_NAME, CONCRETE_PRODUCT);
                        abstractFactoryClass = SciPatternUtil.findPatternLinkSupplier(abstractProductClass, PATTERN_NAME, FACTORY);
                        if (abstractFactoryClass == null) {
                            removeTable();
                            addPropertyValue(CONCRETE_FACTORY, "ConcreteFactory");
                            if (abstractProductClass != null) {
                                addPropertyValue(ABSTRACT_PRODUCT, abstractProductClass.getQualifiedName());
                            }
                            else {
                                addPropertyValue(ABSTRACT_PRODUCT, "AbstractProduct");
                            }
                            setPropertyValue(getConcreteProductPropertyKey(1, 1), getSelectedClass().getQualifiedName());
                        }
                    }
                }

                if (abstractFactoryClass != null) {
                    setPropertyValue(ABSTRACT_FACTORY, abstractFactoryClass.getQualifiedName());
                    Enumeration concreteFactories = SciPatternUtil.findPatternLinkSuppliers(abstractFactoryClass, PATTERN_NAME, CONCRETE_FACTORY);
                    Enumeration abstractProducts = SciPatternUtil.findPatternLinkSuppliers(abstractFactoryClass, PATTERN_NAME, PRODUCT);
                    if (concreteFactories.hasMoreElements() || abstractProducts.hasMoreElements()) {
                        removeTable();
                        Vector abstractProductsVector = new Vector();
                        while (abstractProducts.hasMoreElements()) {
                            SciClass abstractProductClass = (SciClass)abstractProducts.nextElement();
                            abstractProductsVector.addElement(abstractProductClass);
                            addPropertyValue(ABSTRACT_PRODUCT, abstractProductClass.getQualifiedName());
                        }

                        int concreteFactoryNumber = 0;
                        while (concreteFactories.hasMoreElements()) {
                            concreteFactoryNumber++;
                            SciClass concreteFactoryClass = (SciClass)concreteFactories.nextElement();
                            addPropertyValue(CONCRETE_FACTORY, concreteFactoryClass.getQualifiedName());
                            Enumeration concreteProducts = SciPatternUtil.findPatternLinkSuppliers(concreteFactoryClass, PATTERN_NAME, PRODUCT);
                            while (concreteProducts.hasMoreElements()) {
                                SciClass concreteProductClass = (SciClass)concreteProducts.nextElement();
                                SciClass abstractProductClass = SciPatternUtil.findExtendedPatternLinkClient(concreteProductClass,
                                    PATTERN_NAME, CONCRETE_PRODUCT);
                                int index = abstractProductsVector.indexOf(abstractProductClass);
                                if (index >= 0) {
                                    setPropertyValue(getConcreteProductPropertyKey(concreteFactoryNumber, 1+index),
                                        concreteProductClass.getQualifiedName());
                                }
                            }
                        }
                    }
                }
            }
        });
    }

    private PatternUIBuilder    myUIBuilder = null;
    private Vector              myClassRoleVector = new Vector();

    private boolean myInsideListener = false;
    private int myConcreteFactoryNumber = 0;
    private int myAbstractProductNumber = 0;

    private String myPagePropertyKey = null;
    private String myProductPropertyKey = null;

    private static final String PROPERTY_CONDITION  = ".condition";
    private static final String PROPERTY_NAME       = ".name";
    // Subnodes
    private static final String SUBNODE_ITEM        = ".item.";
    private static final String PAGE_ROOT           = "page.";
    private static final String FIRST_PAGE_NAME     = "FirstPatternPage";

    private static final String FACTORY             = "Factory";
    private static final String PRODUCT             = "Product";
    private static final String ABSTRACT_FACTORY    = "Abstract factory";
    private static final String CONCRETE_FACTORY    = "Concrete factory";
    private static final String ABSTRACT_PRODUCT    = "Abstract product";
    private static final String CONCRETE_PRODUCT    = "Concrete product";

    private static final String PAGE_TITLE_PREFIX   = "Concrete products for ";

    // Code patterns
    private static final String PATTERN_RETURN_NEW  = "\nreturn new {0}({1});\n";

    // Message patterns
    private static final String PATTERN_PRODUCT_NOT_DEFINED = "Product ({0}) for Factory ({1}) is not defined.";

    // Messages
    private static final String ONE_ABSTRACT_PRODUCT_SHOULD_BE_DEFINED = "At least one Abstract Product should be defined";
    private static final String ONE_CONCRETE_FACTORY_SHOULD_BE_DEFINED = "At least one Concrete Factory should be defined";

    private void printFactoriesAndProducts() {
        /*
        System.out.println("DEBUG - ABSTRACT_PRODUCT changed:");
        int numProducts = getNumProperties(ABSTRACT_PRODUCT);
        int numFactories = getNumProperties(CONCRETE_FACTORY);
        String name = null;
        System.out.println("numProducts = " + numProducts + ", numFactories = " + numFactories );
        Enumeration factories = propertiesEnumeration(CONCRETE_FACTORY);
        while (factories.hasMoreElements()) {
            name = (String) ((Property)factories.nextElement()).getValue();
            System.out.println("concreteFactoryName = <" + name + ">");

            Enumeration products = propertiesEnumeration(ABSTRACT_PRODUCT);
            while (products.hasMoreElements()) {
                name = (String) ((Property)products.nextElement()).getValue();
                System.out.println("    abstractProductName = <" + name + ">");
            }
        }
        System.out.println("-----\n");
        */
    }

}
