001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.util.common.transfer;
018
019import com.echothree.model.control.core.common.transfer.EntityInstanceTransfer;
020import com.echothree.util.common.string.StringUtils;
021import com.echothree.util.common.transfer.BaseTransfer;
022import com.echothree.util.common.transfer.BaseWrapper;
023import java.lang.reflect.InvocationTargetException;
024import java.lang.reflect.Method;
025import java.util.Collection;
026import java.util.HashSet;
027import java.util.Set;
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030
031public class BaseTransferUtils {
032
033    private static Log log = LogFactory.getLog(BaseTransferUtils.class);
034    
035    private BaseTransferUtils() {
036        super();
037    }
038
039    private static class BaseTransferUtilsHolder {
040        static BaseTransferUtils instance = new BaseTransferUtils();
041    }
042
043    public static BaseTransferUtils getInstance() {
044        return BaseTransferUtilsHolder.instance;
045    }
046
047    private void getEntityRefsFromBaseWrapper(EntityRefExclusions entityRefExclusions, Set<String> entityRefs, Set<Object> visitedObjects,
048            BaseWrapper<?> baseWrapper, int indentCount) {
049        Collection<?> collection = baseWrapper.getCollection();
050
051        if(!collection.isEmpty()) {
052            collection.stream().filter((nextDependsOn) -> !visitedObjects.contains(nextDependsOn)).forEach((nextDependsOn) -> {
053                getEntityRefs(entityRefExclusions, entityRefs, visitedObjects, nextDependsOn, indentCount + 1);
054            });
055        }
056    }
057
058    private Set<String> getEntityRefs(EntityRefExclusions entityRefExclusions, Set<String> entityRefs, Set<Object> visitedObjects, Object dependsOn,
059            int indentCount) {
060        if(BaseTransferUtilsDebugFlags.LogVisits) {
061            log.info(StringUtils.getInstance().getIndent(4, indentCount) + "Visiting " + dependsOn);
062        }
063        visitedObjects.add(dependsOn);
064        
065        if(dependsOn instanceof BaseTransfer) {
066            EntityInstanceTransfer entityInstance = ((BaseTransfer)dependsOn).getEntityInstance();
067            boolean includeMethods = true;
068            
069            if(entityInstance != null) {
070                String nextDependsOnEntityRef = entityInstance.getEntityRef();
071
072                if(nextDependsOnEntityRef != null) {
073                    if(entityRefExclusions != null && nextDependsOnEntityRef != null && entityRefExclusions.contains(nextDependsOnEntityRef)) {
074                        if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
075                            log.info(StringUtils.getInstance().getIndent(4, indentCount) + "Excluding " + nextDependsOnEntityRef);
076                        }
077
078                        includeMethods = false;
079                    } else {
080                        if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
081                            log.info(StringUtils.getInstance().getIndent(4, indentCount) + "Including " + nextDependsOnEntityRef);
082                        }
083
084                        entityRefs.add(nextDependsOnEntityRef);
085                     }
086                }
087            }
088            
089            if(includeMethods) {
090                Method[] methods = dependsOn.getClass().getMethods();
091                
092                for(var method : methods) {
093                    String name = method.getName();
094
095                    if(name.startsWith("get")) {
096                        try {
097                            Class<?> returnType = method.getReturnType();
098
099                            if(BaseTransfer.class.isAssignableFrom(returnType)) {
100                                // If it's a BaseTransfer...
101                                BaseTransfer nextDependsOn = (BaseTransfer)method.invoke(dependsOn);
102                                
103                                if(nextDependsOn != null) {
104                                    if(!visitedObjects.contains(nextDependsOn)) {
105                                        getEntityRefs(entityRefExclusions, entityRefs, visitedObjects, nextDependsOn, indentCount + 1);
106                                    } else {
107                                        if(BaseTransferUtilsDebugFlags.LogVisits) {
108                                            log.info(StringUtils.getInstance().getIndent(4, indentCount) + "Already visited " + nextDependsOn);
109                                        }
110                                    }
111                                }
112                            } else if(BaseWrapper.class.isAssignableFrom(returnType)) {
113                                // If it's a BaseWrapper
114                                BaseWrapper<?> baseWrapper = (BaseWrapper<?>)method.invoke(dependsOn);
115
116                                if(baseWrapper != null) {
117                                    if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
118                                        log.info(StringUtils.getInstance().getIndent(4, indentCount) + "BaseWrapper found: " + name.substring(3));
119                                    }
120
121                                    getEntityRefsFromBaseWrapper(entityRefExclusions, entityRefs, visitedObjects, baseWrapper, indentCount);
122                                }
123                            }
124                        } catch(IllegalAccessException iea) {
125                            throw new RuntimeException(iea);
126                        } catch(IllegalArgumentException iea) {
127                            throw new RuntimeException(iea);
128                        } catch(InvocationTargetException ite) {
129                            throw new RuntimeException(ite);
130                        }
131                    }
132                }
133            }
134        } else if(dependsOn instanceof BaseWrapper) {
135            BaseWrapper<?> baseWrapper = (BaseWrapper<?>)dependsOn;
136            
137            if(baseWrapper != null) {
138                if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
139                    log.info(StringUtils.getInstance().getIndent(4, indentCount) + "BaseWrapper passed in");
140                }
141                
142                getEntityRefsFromBaseWrapper(entityRefExclusions, entityRefs, visitedObjects, baseWrapper, indentCount);
143            }
144        }
145        
146        return entityRefs;
147    }
148
149    public Set<String> getEntityRefs(EntityRefExclusions entityRefExclusions, Object object) {
150        Set<String> entityRefs = new HashSet<>();
151        Set<Object> visitedObjects = new HashSet<>();
152
153        if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
154            log.info("Starting getEntityRefs for " + object);
155        }
156        
157        if(object instanceof BaseTransfer || object instanceof BaseWrapper) {
158            getEntityRefs(entityRefExclusions, entityRefs, visitedObjects, object, 1);
159        } else {
160            throw new IllegalArgumentException("Object for getEntityRefs(...) must be a BaseTransfer or BaseWrapper object.");
161        }
162        
163        if(BaseTransferUtilsDebugFlags.LogGetEntityRefs) {
164            log.info("Ending getEntityRefs");
165        }
166        
167        return entityRefs;
168    }
169
170}