/*
* Copyright 2008 FatWire Corporation. All Rights Reserved.
*
* 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.gsf.facade.runtag.asset;
import COM.FutureTense.Interfaces.FTValList;
import COM.FutureTense.Interfaces.ICS;
import COM.FutureTense.Interfaces.IList;
import COM.FutureTense.Util.IterableIListWrapper;
import com.fatwire.assetapi.data.AssetId;
import com.openmarket.xcelerate.asset.AssetIdImpl;
import com.openmarket.xcelerate.publish.Render;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tools.gsf.facade.assetapi.AssetIdUtils;
import tools.gsf.facade.runtag.render.LogDep;
import tools.gsf.facade.sql.IListUtils;
import tools.gsf.facade.sql.Row;
import tools.gsf.facade.sql.TreeHelper;
import tools.gsf.runtime.CSRuntimeException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
/**
* Utilities for working efficiently with the AssetRelationTree.
*
* @author Tony Field
* @since Jun 7, 2009
*/
public final class AssetRelationTreeUtils {
private static final Logger LOG = LoggerFactory.getLogger("tools.gsf.facade.runtag.asset.AssetRelationTreeUtils");
/**
* Get all of the parent assets in the AssetRelationTree for the specified
* asset. Association name is required, but expectedParentType is an
* optional filter argument.
* <p>
* Does not record any asset dependencies
*
* @param ics ICS context
* @param log logger. May be null.
* @param child child asset id
* @param expectedParentType asset type of the parent to be returned. If
* null, type of parent is irrelevant.
* @param associationName name of association to use while looking for
* parents. may not be null
* @return list of parents, never null.
* @see #getParents(ICS, AssetId, String[])
*/
public static List<AssetId> getAssetRelationTreeParents(ICS ics, Logger log, AssetId child, String expectedParentType, String associationName) {
FTValList vl = new FTValList();
vl.setValString("ftcmd", "findnode");
vl.setValString("treename", "AssetRelationTree");
vl.setValString("where", "oid");
vl.setValString("oid", Long.toString(child.getId()));
if (ics.TreeManager(vl)) {
int errno = ics.GetErrno();
if (errno < 0) {
switch (errno) {
case -111:
if (log != null && log.isTraceEnabled()) {
log.trace("Node not found in AssetRelationTree for asset " + child);
}
return Collections.emptyList();
default: {
throw new CSRuntimeException("Failed to look up asset " + child + " in AssetRelationTree.", errno);
}
}
}
// found node. get its parent
IList art = ics.GetList("AssetRelationTree");
ics.RegisterList("AssetRelationTree", null);
List<AssetId> parents = new ArrayList<AssetId>();
if (art == null || !art.hasData() || art.numRows() == 0) {
if (log != null && log.isTraceEnabled()) {
log.trace("Failed to locate " + child + " in AssetRelationTree.");
}
} else {
List<String> childNodeIds = new ArrayList<String>();
for (IList row : new IterableIListWrapper(art)) {
if (child.getType().equals(IListUtils.getStringValue(row, "otype"))) {
String nid = IListUtils.getStringValue(row, "nid");
String ncode = IListUtils.getStringValue(row, "ncode");
if (log != null && log.isTraceEnabled()) {
log.trace("Found " + child + " in AssetRelationTree. Node ID: " + nid + ", ncode: " + ncode + ", expecting ncode: " + associationName);
}
if (associationName.equals(ncode)) {
childNodeIds.add(IListUtils.getStringValue(row, "nid"));
}
}
}
for (String nid : childNodeIds) {
vl.clear();
vl.setValString("ftcmd", "getparent");
vl.setValString("treename", "AssetRelationTree");
vl.setValString("node", nid);
if (ics.TreeManager(vl) && ics.GetErrno() >= 0) {
art = ics.GetList("AssetRelationTree");
ics.RegisterList("AssetRelationTree", null);
AssetId parent = new AssetIdImpl(IListUtils.getStringValue(art, "otype"), Long.valueOf(IListUtils.getStringValue(art, "oid")));
if (log != null && log.isTraceEnabled()) {
log.trace(child + " in AssetRelationTree has a parent " + parent);
}
if (expectedParentType == null) {
parents.add(parent);
} else {
if (expectedParentType.equals(parent.getType())) {
parents.add(parent);
} else {
if (log != null && log.isDebugEnabled()) {
log.debug("Parent " + parent + " is not of the expected type (" + expectedParentType + ") so it is being excluded from the return list for child: " + child);
}
}
}
} else {
throw new CSRuntimeException("Failed to look up parent of article " + child + " in AssetRelationTree. TreeManager call failed unexpectedly", ics.GetErrno());
}
}
}
return parents;
} else {
throw new CSRuntimeException("Failed to look up article " + child + " in AssetRelationTree. TreeManager call failed unexpectedly", ics.GetErrno());
}
}
/**
* Look up parents in Asset Relation Tree for the specified child. Records asset dependencies as required (either a
* qualified or an unqualified unknowndeps).
*
* @param ics context
* @param child asset id that will have its parents retrieved
* @param associationName name of association to use for lookup. May not ever be null.
* @return list of parents, never null.
*/
public static Collection<AssetId> getParents(ICS ics, AssetId child, String... associationName) {
// validate input
if (ics == null) {
throw new IllegalArgumentException("ICS cannot be null");
}
if (child == null) {
throw new IllegalArgumentException("Child asset id is required");
}
if (associationName == null) {
throw new IllegalArgumentException("Association name may not be null");
}
List<String> assocNames = Arrays.asList(associationName); // so lame...
Collection<AssetId> parents = new HashSet<AssetId>();
for (Row childInfo : TreeHelper.findNode(ics, "AssetRelationTree", child)) {
// right assoc name?
String ncode = childInfo.getString("ncode");
if (!assocNames.contains(ncode)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Asset " + child + " with node " + childInfo.getString("nid") + " is not the child of any other asset using the association name " + assocNames + ". (This node is for the name " + ncode + ".)");
}
// nope...
continue;
}
// Yup. Find its parent.
for (Row parentInfo : TreeHelper.findParents(ics, "AssetRelationTree", childInfo.getString("nid"))) {
AssetId parent = AssetIdUtils.createAssetId(parentInfo.getString("otype"), parentInfo.getString("oid"));
if (LOG.isTraceEnabled()) {
LOG.trace("Found parent " + parent + " of child " + child + " with association name " + ncode);
}
parents.add(parent);
}
}
// log dep on child asset
LogDep.logDep(ics, child);
// log dep on context
// todo: inspect the specified association definitions in "associationName" and only
// record asset-type specific deps instead of unknowndep if possible
Render.UnknownDeps(ics);
if (LOG.isDebugEnabled()) {
LOG.debug("Looked up child asset " + child + " in AssetRelationTree for parents with the association names " + Arrays.asList(associationName) + " and found " + parents.size() + " results. Details: " + (LOG.isTraceEnabled() ? parents : ""));
}
return parents;
}
}