Copying Attribute Values Up a Link

This script was developed to support parallel development on a project where requirements were branched and merged. This used a specific (one-to-one) link module to link master and branched requirements.

This script provides a simple to use dialog that enables you to copy attribute values from the branched requirement back up to the master copy. It assumes that each requirement is a branched copy of a master requirement and that they are linked one-to-one.

//   Copy Attributes Up

/*
	Copy attribute values from (in) linked requirements to the current module.

	Provides a dialog to control the update of the master copy of requirements from
	branched requirements that were previously flowed down..

	Tony Goodman	Initial Version
*/

// Current module
Module masterModule = null

// dialog box elements
DB   dbMain   = null
DBE dbeDialogLabel = null
DBE dbeFrame = null
DBE dbeMasterModuleFrame = null
DBE dbeMasterModule = null
DBE dbeBranchedRequirementsFrame = null
DBE dbeRelationships = null
DBE dbeSpacer = null
DBE dbeBranchedModules = null
DBE dbeAttributes = null
DBE dbeUpdate = null
DBE dbeSelectAll = null
DBE dbeClearAll = null

string dummy[] = {}

Skip skipMasterAttributes = createString
Skip skipBranchedAttributes = createString
Skip skipRelationships = createString
Skip skipBranchedModules = createString
Skip skipSelectedBranchedModules = createString
Skip skipSelectedAttributes = createString

bool relationshipsFound = false

string selectedRelationship = ""

Buffer errBuf = create
int errors = 0

int objectsUpdated = 0

/**********************
	getAttributes
**************************/
string getAttributes()
{
	string mName = ""
	Module branchedModule = null
	AttrDef ad = null
	string attr = ""
	int i = 0

	empty(dbeAttributes)

	if (!relationshipsFound)
	{
		inactive(dbeAttributes)
		return("")
	}

	delete(skipBranchedAttributes)
	skipBranchedAttributes = createString

	for mName in skipSelectedBranchedModules do
	{
		branchedModule = read(mName, false)

		if (null branchedModule)
		{
			return("Error opening module:\n" mName)
		}

		(current ModuleRef__) = branchedModule
		filtering off
		outlining off

		for ad in branchedModule do
		{
			if (!ad.object) continue
			if (ad.name == "Absolute Number") continue
			if (ad.system) continue

			put(skipBranchedAttributes, ad.name, ad.name)
		}
	}

	delete(skipMasterAttributes)
	skipMasterAttributes = createString

	(current ModuleRef__) = masterModule

	for ad in masterModule do
	{
		if (!ad.object) continue
		if (ad.dxl) continue
		if (ad.name == "Absolute Number") continue
		if (ad.system) continue

		if (!find(skipBranchedAttributes, ad.name)) continue

		put(skipMasterAttributes, ad.name, ad.name)
	}

	put(skipMasterAttributes, "Object Heading", "Object Heading")
	put(skipMasterAttributes, "Object Text", "Object Text")

	// Refresh dialog
	i = 0
	for attr in skipMasterAttributes do
	{
		insert(dbeAttributes, i, attr, iconNone)
		setCheck(dbeAttributes, i, true)
		i++
	}

	return("")
}

/********************
	getBrachedModules
*********************/
void getBrachedModules()
{
	ModName_ mn = null
	Object o = null
	LinkRef lr = null
	string mName = ""
	int i = 0

	empty(dbeBranchedModules)

	if (!relationshipsFound)
	{
		return()
	}

	delete(skipBranchedModules)
	skipBranchedModules = createString

	delete(skipSelectedBranchedModules)
	skipSelectedBranchedModules = createString

	for o in masterModule do
	{
		for lr in o <- "*" do
		{
			if (name(module(lr)) != selectedRelationship) continue

			mn = source(lr)

			put(skipBranchedModules, fullName(mn), fullName(mn))
		}
	}

	i = 0
	for mName in skipBranchedModules do
	{
		insert(dbeBranchedModules, i, mName, iconNone)
		set(dbeBranchedModules, i, true)
		i++

		put(skipSelectedBranchedModules, mName, mName)
	}
}

/*****************
	getRelationships
*********************/
void getRelationships()
{
	Object o = null
	LinkRef lr = null
	int i = 0
	string relationship = ""

	delete(skipRelationships)
	skipRelationships = createString

	for o in masterModule do
	{
			for lr in o <- "*" do
			{
				put(skipRelationships, name(module(lr)), name(module(lr)))
				relationshipsFound = true
			}
	}

	if (relationshipsFound)
	{
		i = 0

		for relationship in skipRelationships do
		{
			insert(dbeRelationships, i++, relationship)
		}
	}
	else
	{
		insert(dbeRelationships, 0, "<no relationships found>")
		inactive(dbeUpdate)
	}

	set(dbeRelationships, 0)
	selectedRelationship = get(dbeRelationships)
}

/********************
	updateObject
*********************/
void updateObject(Object o, Object othero)
{
	string attr = ""

	for attr in skipSelectedAttributes do
	{
		// ignores attributes that do not exist without error
		copyAttribute_(othero, o, attr)
	}

	objectsUpdated++
}

/******************
	doSelectBranchedModules
*********************/
void doSelectBranchedModules(DBE dbe, int x)
{
	string res = ""
	string mName = ""

	delete(skipSelectedBranchedModules)
	skipSelectedBranchedModules = createString

	for mName in dbeBranchedModules do
	{
		put(skipSelectedBranchedModules, mName, mName)
	}

	res = getAttributes()

	if (res != "")
	{
		infoBox("ERROR: " res)
	}
}

/******************
	doSelectRelationship
*********************/
void doSelectRelationship(DBE dbe)
{
	string res = ""

	selectedRelationship = get(dbeRelationships)

	getBrachedModules()

	res = getAttributes()

	if (res != "")
	{
		infoBox("ERROR: " res)
	}
}

/*********************
	doUpdate
*********************/
void doUpdate(DB db)
{
	ModName_ mn = null
	string res = ""
	Object o = null
	Object othero = null
	Link l = null
	string attr = ""
	string mName = ""
	int i = 0

	if (!confirm("You are about to update master requirements in the current module with values\n" //-
		             "copied from linked (branched) requirements.\n\nAre you sure you want to continue?"))
	{
		return
	}

	delete(skipSelectedAttributes)
	skipSelectedAttributes = createString

	for (i = 0; i < noElems(dbeAttributes); i++)
	{
		if (getCheck(dbeAttributes, i))
		{
			attr = getColumnValue(dbeAttributes, i, 0)
			put(skipSelectedAttributes, attr, attr)
		}
	}

	for o in masterModule do
	{
		for l in o <- "*" do
		{
			if (name(module(l)) != selectedRelationship) continue

			mn = source(l)

			if (!find(skipSelectedBranchedModules, fullName(mn))) continue

			othero = source(l)

			if (null othero)
			{
				errBuf += "Object " identifier(o) " is linked to a null object\n"
				errors++
				continue
			}

			if (isDeleted othero)
			{
				errBuf += "Object " identifier(o) " is linked to a deleted object\n"
				errors++
				continue
			}

			updateObject(o, othero)
		}
	}

	if (errors > 0)
	{
		infoBox("Update complete, but with errors:\n" stringOf(errBuf))
	}
	else
	{
		infoBox("Update completed successfully.\n" //-
		            objectsUpdated " object" (objectsUpdated == 1 ? " has " : "s have " ) //-
		            "been updated\nPlease remember to save your module.")
	}

	release(dbMain)
}

/*****************
	doSelectAll
*********************/
void doSelectAll(DBE dbe)
{
	int i = 0

	for (i = 0; i < noElems(dbeAttributes); i++)
	{
		setCheck(dbeAttributes, i, true)
	}
}

/********************
	doClearAll
*********************/
void doClearAll(DBE dbe)
{
	int i = 0

	for (i = 0; i < noElems(dbeAttributes); i++)
	{
		setCheck(dbeAttributes, i, false)
	}
}

/********************
	doClose
*********************/
void doClose(DB db)
{
	release(dbMain)
}

/********************
	buildDialog
*********************/
void buildDialog()
{
	string res = ""

	dbMain = create(masterModule, "Copy Attributes Up", styleCentered)

	dbeDialogLabel = label(dbMain, "This utility allows you to update attribute values in your master requirements module.\n" //-
												"Values are copied from branched requirements that they are linked to via the selected relationship.\n" //-
												"Only values for the selected attributes are compared and updated.")

	dbeFrame = frame(dbMain, "", 450, 460)
	dbeFrame->"top"->"flush"->dbeDialogLabel
	dbeFrame->"left"->"form"
	dbeFrame->"right"->"form"
	dbeFrame->"bottom"->"form"

	dbeMasterModuleFrame = frame(dbMain, "Master requirements module", 400, 20)
	dbeMasterModuleFrame->"top"->"inside"->dbeFrame
	dbeMasterModuleFrame->"left"->"inside"->dbeFrame
	dbeMasterModuleFrame->"right"->"inside"->dbeFrame
	dbeMasterModuleFrame->"bottom"->"unattached"

	dbeMasterModule = field(dbMain, "", fullName(masterModule), 50, true)
	dbeMasterModule->"top"->"inside"->dbeMasterModuleFrame
	dbeMasterModule->"left"->"inside"->dbeMasterModuleFrame
	dbeMasterModule->"right"->"inside"->dbeMasterModuleFrame
	dbeMasterModule->"bottom"->"inside"->dbeMasterModuleFrame

	dbeBranchedRequirementsFrame = frame(dbMain, "Branched requirements", 400, 360)
	dbeBranchedRequirementsFrame->"top"->"flush"->dbeMasterModuleFrame
	dbeBranchedRequirementsFrame->"left"->"inside"->dbeFrame
	dbeBranchedRequirementsFrame->"right"->"inside"->dbeFrame
	dbeBranchedRequirementsFrame->"bottom"->"inside"->dbeFrame

	dbeRelationships = choice(dbMain, "Relationship:", dummy, 0, 30, false)
	dbeRelationships->"top"->"inside"->dbeBranchedRequirementsFrame
	dbeRelationships->"left"->"inside"->dbeBranchedRequirementsFrame
	dbeRelationships->"right"->"inside"->dbeBranchedRequirementsFrame
	dbeRelationships->"bottom"->"unattached"

	dbeBranchedModules = listView(dbMain, listViewOptionMultiselect, 400, 5, dummy)
	dbeBranchedModules->"top"->"flush"->dbeRelationships
	dbeBranchedModules->"left"->"inside"->dbeBranchedRequirementsFrame
	dbeBranchedModules->"right"->"inside"->dbeBranchedRequirementsFrame
	dbeBranchedModules->"bottom"->"unattached"

	dbeAttributes = listView(dbMain, listViewOptionCheckboxes | listViewOptionMultiselect, 400, 10, dummy)
	dbeAttributes->"top"->"flush"->dbeBranchedModules
	dbeAttributes->"left"->"inside"->dbeBranchedRequirementsFrame
	dbeAttributes->"right"->"inside"->dbeBranchedRequirementsFrame
	dbeAttributes->"bottom"->"unattached"

	dbeSelectAll = button(dbMain, "Select All", doSelectAll)
	dbeSelectAll->"top"->"flush"->dbeAttributes
	dbeSelectAll->"left"->"inside"->dbeBranchedRequirementsFrame
	dbeSelectAll->"right"->"unattached"
	dbeSelectAll->"bottom"->"inside"->dbeBranchedRequirementsFrame

	dbeClearAll = button(dbMain, "ClearAll", doClearAll)
	dbeClearAll->"top"->"flush"->dbeAttributes
	dbeClearAll->"left"->"flush"->dbeSelectAll
	dbeClearAll->"right"->"unattached"
	dbeClearAll->"bottom"->"inside"->dbeBranchedRequirementsFrame

	dbeUpdate = apply(dbMain, "Update", doUpdate)

	realize(dbMain)

	insertColumn(dbeAttributes, 0, "Attributes to compare/update", 430, iconNone)
	insertColumn(dbeBranchedModules, 0, "Branched requirements modules", 430, iconNone)

	set(dbeRelationships, doSelectRelationship)
	set(dbeBranchedModules, doSelectBranchedModules, doSelectBranchedModules, doSelectBranchedModules)

	getRelationships()

	getBrachedModules()

	getAttributes()

	block(dbMain)
	destroy(dbMain)
	dbMain = null
}

/***************************
	requirementsFlowUp
***************************/
void requirementsFlowUp()
{
	masterModule = current Module

	if (null masterModule || !isEdit masterModule)
	{
		infoBox("This utility can only be run from a formal module open for exclusive edit.")
		halt
	}

	(current ModuleRef__) = masterModule

	buildDialog()
}

/***************************
	MAIN
***************************/
requirementsFlowUp()