All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tools.xor.action.ListUpdateAction Maven / Gradle / Ivy

/**
 * XOR, empowering Model Driven Architecture in J2EE applications
 *
 * Copyright (c) 2012, Dilip Dalton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and limitations 
 * under the License.
 */

package tools.xor.action;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;

import tools.xor.BusinessObject;
import tools.xor.CallInfo;
import tools.xor.ExtendedProperty;
import tools.xor.ProcessingStage;
import tools.xor.operation.GraphTraversal;
import tools.xor.util.ClassUtil;
import tools.xor.util.Constants;


public class ListUpdateAction extends CollectionUpdateAction {
	private static final Logger logger = LogManager.getLogger(new Exception()
			.getStackTrace()[0].getClassName());
	private static final Logger owLogger = LogManager.getLogger(Constants.Log.OBJECT_WALKER);

	private PropertyKey key;
	private List        addActions = new ArrayList();
	private List     removeActions = new ArrayList();

	public ListUpdateAction(PropertyKey key) {
		this.key = key;
	}

	@Override
	public void execute() {
		boolean collectionModified = false;

		// TODO: remove elements

		// Sort by position
		Map listOrderedActions = new TreeMap();

		for(AddElementAction action: addActions) 
			listOrderedActions.put(Integer.parseInt(action.getPosition().toString()), action);
		// TODO: Handler conflicts
		for(AddElementAction action: addByOppositeActions) {
			// If the element does have position information, then it cannot be part of a list
			// as the ordering is not known
			if(action.getPosition() != null) {
				listOrderedActions.put(Integer.parseInt(action.getPosition().toString()), action);
			}
		}

		for(AddElementAction action: listOrderedActions.values()) {
			// Add the element to the List
			addToList(action);		
			collectionModified = true;			
		}
		
		// TODO: checkOutOfSync()
	
	}

	private List extractList(BusinessObject bo) {
		List list = new ArrayList();

		if(bo != null) {
			if (bo.getInstance() instanceof List) {
				list = (List)bo.getInstance();
			}
			else if (bo.getInstance() instanceof JSONArray) {
				list = ClassUtil.jsonArrayToList((JSONArray)bo.getInstance());
			}
		}

		return list;
	}

	@Override
	public void processLinks (Map outputMap,
							  BusinessObject input,
							  CallInfo callInfo) throws
		Exception
	{
		List list = extractList(input);

		for(int i = 0; i < list.size(); i++) {
			Object obj = list.get(i);
			processLink((new Integer(i)).toString(), obj, callInfo, outputMap);
		}
	}

	@Override
	public void addAction(Executable action) {

		if(action instanceof AddElementAction)
			addActions.add((AddElementAction) action);
		else if(action instanceof RemoveElementAction)
			removeActions.add((RemoveElementAction) action);		
		else
			throw new IllegalArgumentException("A list can have only add, remove actions");
	}

	@Override
	public PropertyKey getKey() {
		return key;
	}

	public void unlinkElements(CallInfo callInfo) {
		super.unlinkElements(callInfo);

		BusinessObject input  = ((BusinessObject)callInfo.getInput());
		BusinessObject output = ((BusinessObject)callInfo.getOutput());

		if(output == null)
			return;

		List inputList = extractList(input);
		Object outputList = output.getInstance();
		int outputListSize = getSize(outputList);

		if(outputListSize > inputList.size()) { // truncate the remaining portion
			truncateToSize(outputList, inputList.size());
		}
	}

	private static int getSize(Object outputList) {
		if(outputList instanceof List) {
			return ((List)outputList).size();
		} else if(outputList instanceof JSONArray) {
			return ((JSONArray)outputList).length();
		}

		return -1;
	}

	private static void addElement(Object outputList, Object element) {
		if(outputList instanceof List) {
			((List)outputList).add(element);
		} else if(outputList instanceof JSONArray) {
			((JSONArray)outputList).put(element);
		}
	}

	private static void setElement(Object outputList, Object element, int position) {
		if(outputList instanceof List) {
			((List)outputList).set(position, element);
		} else if(outputList instanceof JSONArray) {
			((JSONArray)outputList).put(position, element);
		}
	}

	private static void truncateToSize(Object outputObj, int toSize) {
		if(outputObj instanceof List ) {
			List outputList = (List) outputObj;
			for (int i = outputList.size(); i > toSize; i--) {
				outputList.remove(i - 1);
			}
		} else if(outputObj instanceof JSONArray) {
			JSONArray outputArray = (JSONArray) outputObj;
			for (int i = outputArray.length(); i > toSize; i--) {
				outputArray.remove(i - 1);
			}
		}
	}

	public void linkElement(CallInfo next, String dynamicProperty, boolean isNew) throws Exception {
		GraphTraversal oper = (GraphTraversal) next.getOperation();

		/*
		BusinessObject targetElement = (BusinessObject) next.getOutput();
		if(next.getSettings().getAction() != AggregateAction.CREATE && targetElement != null) // updating an existing object
			next.setOutput(targetElement);
		else
			next.setOutput(oper.createTarget(next, null));
*/
		if(owLogger.isDebugEnabled()) {
			if(isNew && next.getStage() == ProcessingStage.CREATE) {
				owLogger.debug(next.getIndentString() + "[Creating a new list element]");
			}
		}
		
		oper.processAttribute(next);
		
		// Do not modify the data graph unless we are in the right stage
		if(next.getStage() != ProcessingStage.UPDATE) {
			return;
		}

		// AddElementAction
		int i = Integer.parseInt(dynamicProperty);
		addListElement(next, (BusinessObject) next.getOutput(), i);

	}

	protected void addListElement(CallInfo callInfo, BusinessObject collectionElement, Object position) {
		Object collection = callInfo.getParent().getOutput();
		BusinessObject collectionOwner = (BusinessObject) callInfo.getParent().getParent().getOutput();

		PropertyKey propertyKey = new PropertyKey((BusinessObject) collection, callInfo.getParent().getOutputProperty());
		Executable originalAction = new AddElementAction(propertyKey, collectionElement, collectionOwner, null, position, callInfo.getInputObjectCreator());
		this.addAction(originalAction);

		// Set the backRef of value to point to the parent target (or to the ancester in case of embedded property)
		// TODO: handle MANY_TO_MANY backRef
		if(callInfo.getParent().getOutputProperty().isBiDirectional()) {
			PropertyKey newOppositeKey = new PropertyKey(collectionElement, callInfo.getParent().getOutputProperty().getOpposite());			
			callInfo.getOutputRoot().getObjectPersister().addAction(new SetterAction(callInfo.getSettings(), collectionOwner, newOppositeKey, originalAction));
		}
	}

	protected void addToList(AddElementAction action) {

		BusinessObject collectionOwner   = action.getCollectionOwner();
		Object collection                    = ((ExtendedProperty) action.getKey().getProperty()).getValue(collectionOwner);
		BusinessObject collectionElement = action.getCollectionElement();
		ExtendedProperty collectionProperty  = (ExtendedProperty) action.getKey().getProperty();

		// It is possible for the collection object to be null, if this is due to the MANY_TO_ONE end only being set
		try {
			if(collection == null) {
				collection = createCollection(collectionOwner, collectionProperty);
			} else {
				collection = ClassUtil.getInstance(collection);
			}
		} catch(Exception e) {
			throw ClassUtil.wrapRun(e);
		}

		int listSize = getSize(collection);
		int position = Integer.parseInt(action.getPosition().toString());
		if(listSize == position ) {
			addElement(collection, ClassUtil.getInstance(collectionElement));
		} else if (listSize > position)
		{
			// replace the item at the postion
			setElement(collection, ClassUtil.getInstance(collectionElement), position);
		} else {
			throw new IllegalArgumentException(
				"Trying to add a list element at the wrong position: " + action.getPosition() + ", list size: "
					+ listSize
					+ ". Check if the ORM is using a different API for populating this property - "
					+ collectionProperty.getContainingType().getName() + "#" + collectionProperty.getName()
			);
		}

		if(collectionProperty.getPositionProperty() != null) {
			collectionElement.set(collectionProperty.getPositionProperty(), position);
		}
	}		
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy