/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.core.factories;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import com.salesforce.ide.api.metadata.types.MetadataExt;
import com.salesforce.ide.core.internal.factories.ApplicationContextFactory;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.ComponentList;
import com.salesforce.ide.core.model.IComponent;
import com.salesforce.ide.core.model.ProjectPackage;
import com.salesforce.ide.core.remote.metadata.CustomObjectNameResolver;
import com.salesforce.ide.core.remote.metadata.FileMetadataExt;
import com.sforce.soap.metadata.FileProperties;
/**
* Encapsulates functionality related to managing Salesforce.com object types.
*
* @author cwall
*/
public class ComponentFactory extends ApplicationContextFactory {
private static final Logger logger = Logger.getLogger(ComponentFactory.class);
private ComponentList componentRegistry = null;
private ComponentList enabledRegisteredComponentRegistry = null;
private String metadataFileExtension = null;
private String[] defaultDisabledComponentTypes = null;
private List<String> wildCardSupportedComponentTypes = null;
private ComponentList enabledRegisteredWildCardComponentRegistry = null;
// C O N S T R U C T O R S
public ComponentFactory() {}
// M E T H O D S
public String getMetadataFileExtension() {
return metadataFileExtension;
}
public void setMetadataFileExtension(String metadataFileExtension) {
this.metadataFileExtension = metadataFileExtension;
}
public String[] getDefaultDisabledComponentTypes() {
return defaultDisabledComponentTypes;
}
public void setDefaultDisabledComponentTypes(String[] defaultDisabledComponentTypes) {
this.defaultDisabledComponentTypes = defaultDisabledComponentTypes;
}
// lookup method injection by container
public ComponentList getComponentListInstance() {
return new ComponentList();
}
public ComponentList getComponentRegistry() {
return componentRegistry;
}
public int getComponentRegistrySize() {
return Utils.isNotEmpty(componentRegistry) ? componentRegistry.size() : 0;
}
public void setComponentRegistry(ComponentList componentRegistry) {
this.componentRegistry = componentRegistry;
if (logger.isDebugEnabled()) {
StringBuffer strBuff = new StringBuffer();
if (Utils.isEmpty(this.componentRegistry)) {
strBuff.append("No components registered");
} else {
strBuff.append("Registered the following components:");
int i = 0;
for (IComponent component : this.componentRegistry) {
strBuff.append("\n (").append(++i).append(") ").append(component.toStringLite());
}
}
logger.debug(strBuff.toString());
}
}
public ComponentList getRegisteredComponents() {
return componentRegistry;
}
public ComponentList getRegisteredComponentsForComponentTypes(String[] componentTypes) {
if (Utils.isEmpty(componentRegistry)) {
return null;
}
if (Utils.isEmpty(componentTypes)) {
return componentRegistry;
}
ComponentList components = getComponentListInstance();
for (String componentType : componentTypes) {
for (IComponent component : componentRegistry) {
if (componentType.equals(component.getComponentType())) {
components.add((Component) component);
if (logger.isDebugEnabled()) {
logger.debug("Added '" + component.getComponentType() + "' as registered type");
}
break;
}
}
}
return components;
}
public List<String> getWildcardSupportedComponentTypes() {
if (Utils.isNotEmpty(wildCardSupportedComponentTypes)) {
return wildCardSupportedComponentTypes;
}
wildCardSupportedComponentTypes = new ArrayList<String>();
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return wildCardSupportedComponentTypes;
}
for (IComponent component : componentRegistry) {
if (component.isWildCardSupported() && !component.isInternal()) {
wildCardSupportedComponentTypes.add(component.getComponentType());
}
}
// Add the subcomponent types that aren't supported as first-class
// components, but which we think we can use this way. See changelist 1452239.
wildCardSupportedComponentTypes.addAll(getSubComponentTypes());
return wildCardSupportedComponentTypes;
}
public List<String> getRegisteredComponentTypes() {
List<String> componentTypes = new ArrayList<String>();
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return componentTypes;
}
return componentRegistry.getComponentTypes();
}
public List<String> getInternalComponentTypes() {
List<String> componentTypes = new ArrayList<String>();
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return componentTypes;
}
for (IComponent component : componentRegistry) {
if (component.isInternal()) {
componentTypes.add(component.getComponentType());
}
}
return componentTypes;
}
public List<String> getEnabledRegisteredComponentTypes() {
List<String> componentTypes = new ArrayList<String>();
ComponentList enabledComponents = getEnabledRegisteredComponents();
if (Utils.isEmpty(enabledComponents)) {
logger.warn("Enabled omponent registry is null or empty");
return componentTypes;
}
return enabledComponents.getComponentTypes();
}
public ComponentList getEnabledRegisteredComponents() {
if (enabledRegisteredComponentRegistry != null) {
return enabledRegisteredComponentRegistry;
}
enabledRegisteredComponentRegistry = getComponentListInstance();
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return enabledRegisteredComponentRegistry;
}
for (Component component : componentRegistry) {
String componentType = component.getComponentType();
if (!componentType.equals(component.getComponentType())) {
continue;
}
if (!isValidComponentByComponentType(component) && isDisabledComponentType(componentType)) {
if (logger.isInfoEnabled()) {
logger.info("'" + component.getComponentType()
+ "' is not IDE-default enabled. Excluding from registered list.");
}
continue;
}
enabledRegisteredComponentRegistry.add(component);
}
return enabledRegisteredComponentRegistry;
}
public ComponentList getEnabledRegisteredWildCardSupportedComponents() {
if (enabledRegisteredWildCardComponentRegistry != null) {
return enabledRegisteredWildCardComponentRegistry;
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return enabledRegisteredWildCardComponentRegistry;
}
enabledRegisteredWildCardComponentRegistry = getComponentListInstance();
for (Component component : componentRegistry) {
String componentType = component.getComponentType();
if (!componentType.equals(component.getComponentType())) {
continue;
}
if (!isValidComponentByComponentType(component) || isDisabledComponentType(componentType)
|| !isWildCardSupportedComponentType(componentType)) {
if (logger.isInfoEnabled()) {
logger.info("'" + component.getComponentType()
+ "' is not IDE-default enabled. Excluding from registered list.");
}
continue;
}
enabledRegisteredWildCardComponentRegistry.add(component);
}
return enabledRegisteredWildCardComponentRegistry;
}
public ComponentList getEnabledRegisteredComponents(String[] componentTypes) {
ComponentList enabledComponents = getEnabledRegisteredComponents();
if (Utils.isEmpty(enabledComponents) || Utils.isEmpty(componentTypes)) {
return enabledComponents;
}
ComponentList components = getComponentListInstance();
for (String componentType : componentTypes) {
for (Component enabledComponent : enabledComponents) {
if (componentType.equals(enabledComponent.getComponentType())) {
components.add(enabledComponent);
if (logger.isDebugEnabled()) {
logger.debug("Added '" + enabledComponent.getComponentType() + "' as registered type");
}
break;
}
}
}
return components;
}
public String getRegisteredComponentFolderByComponentType(String componentType) {
if (Utils.isEmpty(componentType)) {
logger.error("Object type cannot be null");
throw new IllegalArgumentException("Object type cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
for (IComponent component : componentRegistry) {
if (componentType.equals(component.getComponentType())) {
return component.getDefaultFolder();
}
}
return null;
}
public List<String> getDeleteableRegisteredComponentTypes() {
List<String> componentTypes = new ArrayList<String>();
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return componentTypes;
}
for (IComponent component : componentRegistry) {
if (component.isRemoteDeleteable()) {
componentTypes.add(component.getComponentType());
}
}
return componentTypes;
}
public boolean isWildCardSupportedComponentType(String componentType) {
List<String> componentTypes = getWildcardSupportedComponentTypes();
return componentTypes.contains(componentType);
}
public boolean isDisabledComponentType(String componentType) {
if (Utils.isEmpty(defaultDisabledComponentTypes)) {
return false;
}
for (String disabledComponentType : defaultDisabledComponentTypes) {
if (componentType.equals(disabledComponentType)) {
return true;
}
}
return false;
}
public boolean isValidComponentByComponentType(IComponent component) {
return !component.isInternal() && !component.isPackageManifest() && !component.isPackageManifest();
}
public boolean isEnabledComponentType(String componentType) {
ComponentList components = getEnabledRegisteredComponents(new String[] { componentType });
if (Utils.isEmpty(components)) {
if (logger.isDebugEnabled()) {
logger.debug("Object type '" + componentType + "' is not IDE-default enabled");
}
return false;
}
return true;
}
public boolean isRegisteredComponentType(String componentType) {
return Utils.isNotEmpty(componentRegistry) && componentRegistry.hasComponentType(componentType);
}
public String getComponentTypeByFolderName(String folderName) throws FactoryException {
if (Utils.isEmpty(folderName)) {
logger.error("Folder name is null");
throw new IllegalArgumentException("Folder name cannot be null");
}
Component component = getComponentByFolderName(folderName);
if (component == null) {
logger.warn("Unable to determine object type for folder '" + folderName + "'");
return null;
}
return component.getComponentType();
}
public List<String> getComponentFolderNames(String[] componentTypes) throws FactoryException {
if (Utils.isEmpty(componentTypes)) {
return null;
}
List<String> folderNames = new ArrayList<String>();
Component component = null;
for (String componentType : componentTypes) {
component = getComponentFactory().getComponentByComponentType(componentType);
if (component != null) {
folderNames.add(component.getDefaultFolder());
}
}
return folderNames;
}
public String getComponentFolderName(String componentType) throws FactoryException {
if (Utils.isEmpty(componentType)) {
return null;
}
Component component = getComponentFactory().getComponentByComponentType(componentType);
if (component != null) {
return component.getDefaultFolder();
}
return null;
}
public Component getComponentById(String id) {
return getComponentById(id, null);
}
public Component getComponentById(String id, String componentType) {
Component component = null;
// if id is not found, get unknown type and set object type manually
if (Utils.isEmpty(id)) {
logger.warn("No id found for object type '" + componentType + "' - creating component from '"
+ Constants.UNKNOWN_COMPONENT_TYPE + "' component");
id = Constants.UNKNOWN_COMPONENT_TYPE;
component = getComponentBean(id);
if (Utils.isNotEmpty(componentType)) {
component.setComponentType(componentType);
component.setDisplayName(componentType);
}
} else {
component = getComponentBean(id);
}
return component;
}
/**
* Create new component instance base on given extension.
*
* @param extension
* @return
* @throws FactoryException
*/
public Component getComponentByExtension(String extension) throws FactoryException {
if (Utils.isEmpty(extension)) {
logger.error("Extension cannot be null");
throw new IllegalArgumentException("Extension cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Looking up component for extension '" + extension + "'");
}
String id = getComponentByIdByExtension(extension);
// get component from context
Component component = getComponentById(id, id);
if (component == null) {
logger.error("Bean not found for id '" + id);
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Found component of type " + component.getFullDisplayName() + " for id '" + id
+ "' and extension '" + extension + "'");
}
checkComponentMetadata(extension, component);
return component;
}
public String getComponentByIdByExtension(String extension) throws FactoryException {
if (Utils.isEmpty(extension)) {
logger.error("Extension cannot be null");
throw new IllegalArgumentException("Extension cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
String id = null;
for (IComponent component : componentRegistry) {
if (extension.equals(component.getFileExtension()) || isComponentMetadataMatch(extension, component)) {
id = component.getComponentType();
break;
}
}
return id;
}
/**
* Create new component instance base on given object type.
*
* @param componentType
* @return
* @throws FactoryException
*/
public Component getComponentByComponentType(String componentType) throws FactoryException {
if (Utils.isEmpty(componentType)) {
logger.error("Component type cannot be null");
throw new IllegalArgumentException("Object type cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
// get component from context
Component component = getComponentById(componentType, componentType);
if (component == null) {
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Found component of type " + component.getFullDisplayName() + " for id '" + componentType
+ "' and object type '" + componentType + "'");
}
return component;
}
public Component getComponentByComponentTypeClass(MetadataExt metadataExt) throws FactoryException, JAXBException {
if (metadataExt == null) {
logger.error("MetadataExt instance cannot be null");
throw new IllegalArgumentException("MetadataExt instance cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
String componentType = metadataExt.getClass().getSimpleName();
Component component = getComponentByComponentType(componentType);
if (component != null) {
component.setBody(metadataExt.getXMLString());
component.setName(metadataExt.getFullName());
}
return component;
}
/**
* Create new component instance base on given file path.
*
* @param filePath
* @return
* @throws FactoryException
*/
// REVIEWME: the following method was primarly based on the default folder name for determining component type.
// instead, why not use file extension?
public Component getComponentByFilePath(String filePath) throws FactoryException {
if (Utils.isEmpty(filePath)) {
logger.error("Filepath cannot be null");
throw new IllegalArgumentException("Filepath cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
// ignore src/ and referenced.../ part of filepath
String tmpfilePath = Utils.stripSourceFolder(filePath);
if (logger.isDebugEnabled()) {
logger.debug("Looking up component for filepath '" + tmpfilePath + "'");
}
// id to use for bean lookup
String id = null;
// use parts for further investigation, if needed, and to determine if component is a folder component
String[] pathParts = null;
if (tmpfilePath.contains(Constants.FOWARD_SLASH)) {
pathParts = tmpfilePath.split(Constants.FOWARD_SLASH);
}
// quick wins: package.xml and inspect file extension
if (filePath.endsWith(Constants.PACKAGE_MANIFEST_FILE_NAME)) {
id = Constants.PACKAGE_MANIFEST;
} else {
String fileExtension =
Utils.getExtensionFromFilePath(Utils.isNotEmpty(pathParts) ? pathParts[pathParts.length - 1]
: tmpfilePath);
if (Utils.isNotEmpty(fileExtension)) {
if (Constants.RULE_EXTENSIONS.contains(fileExtension)) {
id = getComponentTypeByFolderName(pathParts[0]);
} else {
id = getComponentByIdByExtension(fileExtension);
}
}
}
// if id still not determined, look at path, specifically the parent folders
if (Utils.isEmpty(id) && Utils.isNotEmpty(pathParts)) {
// first the component folder
// handles:
// <component-folder>/
// <component-folder>/<component>
// <component-folder>/<user-folder>/<component>
id = getComponentIdForPathFolderPart(pathParts[0]);
// it could be that the component folder is two back if the component is in a user-defined folder
// handles:
// <pkg>/<component-folder>
// <pkg>/<component-folder>/<component>
// <pkg>/<component-folder>/<user-folder>/<component>
if (Utils.isEmpty(id) && pathParts.length > 1) {
id = getComponentIdForPathFolderPart(pathParts[1]);
}
}
// unable to determine
if (Utils.isEmpty(id)) {
logger.warn("No id found for filepath '" + filePath + "', tagging as component of type '"
+ Constants.UNKNOWN_COMPONENT_TYPE + "'");
id = Constants.UNKNOWN_COMPONENT_TYPE;
}
// if component is a folder, get folder component and set sub type
Component component = getComponentBean(id);
if (isFolderComponent(component, pathParts)) {
if (logger.isDebugEnabled()) {
logger.debug("Getting '" + id + "' folder bean for path '" + tmpfilePath + "'");
}
component = getComponentBean(Constants.FOLDER);
component.setSecondaryComponentType(id);
setFolderName(component, pathParts);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Getting bean for id '" + id + "'");
}
component = getComponentBean(id);
}
if (component == null) {
logger.error("Bean not found for id '" + id + "' found for filepath '" + filePath + "'");
return null;
}
if (CustomObjectNameResolver.getCheckerForStandardObject().check(Utils.getNameFromFilePath(filePath),
component.getComponentType())) {
component = getComponentBean(Constants.STANDARD_OBJECT);
}
component.setFilePath(filePath);
component.setNameFromFilePath(filePath);
component.setManagedFromFilePath(filePath);
if (component.isWithinFolder()) {
setFolderName(component, pathParts);
}
// determine if file is metadata
checkComponentMetadata(filePath, component);
if (logger.isDebugEnabled()) {
logger.debug("Created component of type '" + component.getComponentType() + "' for id '" + id
+ "' and filepath '" + filePath + "'");
}
return component;
}
private String getComponentIdForPathFolderPart(String pathComponentFolderPart) {
if (Utils.isNotEmpty(pathComponentFolderPart)) {
for (IComponent component : componentRegistry) {
if (Utils.isNotEmpty(pathComponentFolderPart)
&& pathComponentFolderPart.equals(component.getDefaultFolder())) {
return component.getComponentType();
}
}
}
if (logger.isInfoEnabled()) {
logger.info("Did not find bean id for path part '" + pathComponentFolderPart + "'");
}
return null;
}
private void setFolderName(Component component, String[] pathParts) {
if (Utils.isEmpty(pathParts) || pathParts.length < 2) {
return;
}
String folderName = pathParts[1];
if (folderName.endsWith(component.getMetadataFileExtensionPart())) {
folderName = folderName.substring(0, folderName.indexOf(component.getMetadataFileExtensionPart()));
}
component.setParentFolderNameIfComponentMustBeInFolder(folderName);
}
public boolean isFolderComponent(String filePath) throws FactoryException {
if (Utils.isEmpty(filePath)) {
logger.error("Filepath cannot be null");
throw new IllegalArgumentException("Filepath cannot be null");
}
if (!filePath.endsWith(metadataFileExtension)) {
return false;
}
List<String> folderNames = getFolderNamesForFolderComponents();
String[] folderTokens = filePath.split("/");
for (String folderName : folderNames) {
if (folderName.equals(folderTokens[folderTokens.length - 1])) {
if (logger.isDebugEnabled()) {
logger.debug("Filepath '" + filePath + "' is a Folder component");
}
return true;
}
}
return false;
}
public boolean isFolderComponent(Component component, String[] filePath) throws FactoryException {
if (component == null || Utils.isEmpty(filePath)) {
return false;
}
// only consider components configured as within a folder
if (!component.isWithinFolder()) {
return false;
}
// filepath has to be 2 < len < 4, see below
if (filePath.length == 1 || filePath.length == 4) {
return false;
}
/* handles:
* when filePath.length == 2 and component.isWithinFolder()==true, component will always be a user-folder
* <component-folder>/<user-folder>/<component>
*/
if (filePath.length == 2) {
return true;
}
/* when filePath.length == 3, component is user-folder when default component folder is in position 2
* <component-folder>/<user-folder>/README<component>
* <pkg>/<component-folder>/<user-folder>/<component>
*
* the one risk here being that user-folder name == default folder
*/
if (filePath.length == 3 && filePath[1].equals(component.getDefaultFolder())) {
return true;
}
return false;
}
public boolean isFolderSubComponentType(String componentType) {
if (Utils.isEmpty(componentType)) {
logger.warn("Component registry is null or empty");
return false;
}
Component component = getComponentBean(componentType);
return component != null && component.isWithinFolder();
}
public List<String> getFolderNamesForFolderComponents() {
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
List<String> folderNames = new ArrayList<String>();
for (IComponent component : componentRegistry) {
if (component.isWithinFolder()) {
folderNames.add(component.getDefaultFolder());
}
}
return folderNames;
}
public ComponentList getFolderComponents() {
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
ComponentList components = getComponentListInstance();
for (Component component : componentRegistry) {
if (component.isWithinFolder()) {
components.add(component);
}
}
return components;
}
public boolean isComponentMetadata(IFile file) {
return file.getName().endsWith(metadataFileExtension);
}
/**
* Create new component instance base on given folder name.
*
* @param folderName
* @return
* @throws FactoryException
*/
public Component getComponentByFolderName(String folderName) throws FactoryException {
if (Utils.isEmpty(folderName)) {
logger.error("Folder name cannot be null");
throw new IllegalArgumentException("Folder name cannot be null");
}
if (Utils.isEmpty(componentRegistry)) {
logger.warn("Component registry is null or empty");
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Looking up component for folder name '" + folderName + "'");
}
String id = null;
for (IComponent component : componentRegistry) {
if (folderName.equals(component.getDefaultFolder())) {
id = component.getComponentType();
break;
}
}
// get component from context
Component component = getComponentById(id, id);
if (component == null) {
logger.error("Bean not found for id '" + id);
return null;
}
// get empty component for the folder's sub-type to load defaults, if applicable
setFolderComponentDefaults(component);
if (logger.isDebugEnabled()) {
logger.debug("Found component of type " + component.getFullDisplayName() + " for id '" + id
+ "' and folder name '" + folderName + "'");
}
return component;
}
/**
* Get existing component with given file resource. The component's body will be included.
*
* @param file
* @return
* @throws FactoryException
*/
public Component getComponentFromFile(IFile file) throws FactoryException {
return getComponentFromFile(file, true);
}
/**
* Get existing component with given file resource. Boolean determines whether the body is included.
*
* @param file
* @param includeBody
* @return
* @throws FactoryException
*/
public Component getComponentFromFile(IFile file, boolean includeBody) throws FactoryException {
if (file == null) {
logger.error("File cannot be null");
throw new IllegalArgumentException("File cannot be null");
}
// get object type specific instance of file and set file to new object
Component component = getComponentByFilePath(file.getProjectRelativePath().toPortableString());
if (component == null) {
throw new FactoryException("Unable to create component from path "
+ file.getProjectRelativePath().toPortableString());
}
component.setFileResource(file);
try {
component.loadFromFile(includeBody);
} catch (Exception e) {
throw new FactoryException(e);
}
if (Utils.isEmpty(component.getPackageName())) {
String packageName = getProjectService().getPackageName(file.getProject());
component.setPackageName(packageName);
}
// get empty component for the folder's sub-type to load defaults, if applicable
setFolderComponentDefaults(component);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + component.getFullDisplayName() + " from file '" + file.getName() + "'");
}
return component;
}
public Component getComponentFromSubFolder(IFolder folder, boolean includeBody) throws FactoryException {
if (folder == null) {
logger.error("Folder cannot be null");
throw new IllegalArgumentException("Folder cannot be null");
}
// get associated metadata file
String filePath =
folder.getProjectRelativePath().toPortableString() + Constants.DEFAULT_METADATA_FILE_EXTENSION;
IFile folderMetadataFile = folder.getProject().getFile(filePath);
Component component = null;
if (folderMetadataFile != null && folderMetadataFile.exists()) {
component = getComponentFromFile(folderMetadataFile, true);
if (component == null) {
logger.warn("Unable to get component metadata from file '"
+ folderMetadataFile.getProjectRelativePath().toPortableString() + "'");
return null;
}
} else if (folder.getParent() != null && folder.getParent().getType() == IResource.FOLDER
&& folder.getParent().exists()) {
logger.warn("Unable to get folder's component metadata file '"
+ folderMetadataFile.getProjectRelativePath().toPortableString() + "'");
IFolder parent = (IFolder) folder.getParent();
component = getComponentByFolderName(parent.getName());
if (component == null) {
logger.warn("Unable to get component from parent '" + parent.getName() + "'");
return null;
}
component.setName(folder.getName());
component.setFilePath(folderMetadataFile.getProjectRelativePath().toPortableString());
component.setSecondaryComponentType(component.getComponentType());
component.setComponentType(Constants.FOLDER);
component.setDisplayName(Constants.FOLDER);
String packageName = getProjectService().getPackageName(folder.getProject());
component.setPackageName(packageName);
} else {
logger.warn("Unable to get folder's component metadata file '"
+ folderMetadataFile.getProjectRelativePath().toPortableString() + "' or derive from parent");
return null;
}
// get empty component for the folder's sub-type to load defaults, if applicable
setFolderComponentDefaults(component);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + component.getFullDisplayName() + " from folder '" + folder.getName() + "'");
}
return component;
}
public ComponentList getAssociatedComponents(Component component) throws FactoryException {
if (component == null) {
throw new IllegalArgumentException("Component cannot be null");
}
ComponentList componentList = getComponentListInstance();
// get parent sub-folder component
if (component.isWithinFolder() && component.getFileResource() != null
&& component.getFileResource().getParent() != null
&& component.getFileResource().getParent().getType() == IResource.FOLDER) {
IFolder parentFolder = (IFolder) component.getFileResource().getParent();
Component folderComponent = getComponentFromSubFolder(parentFolder, true);
componentList.add(folderComponent);
}
// could be used for workflow-object, layout-object, etc component
return componentList;
}
private void setFolderComponentDefaults(Component componentMetadata) throws FactoryException {
if (Constants.FOLDER.equals(componentMetadata.getComponentType())
&& Utils.isNotEmpty(componentMetadata.getSecondaryComponentType())) {
Component component = getComponentByComponentType(componentMetadata.getSecondaryComponentType());
if (component != null) {
componentMetadata.setWebComponentTypeUrlPart(component.getWebComponentTypeUrlPart());
componentMetadata.setWebComponentUrlPart(component.getWebComponentUrlPart());
}
}
}
public ComponentList getComponentsFromResources(IResource[] resources) throws FactoryException {
if (Utils.isEmpty(resources)) {
logger.error("Resources cannot be null");
throw new IllegalArgumentException("Resources cannot be null");
}
ComponentList componentList = getComponentListInstance();
for (IResource resource : resources) {
if (resource.getType() != IResource.FILE) {
continue;
}
Component component = getComponentFromFile((IFile) resource, true);
componentList.add(component);
}
return componentList;
}
/**
* Get existing composite component with given file resource. Boolean determines whether the body is included.
*
* @param file
* @param includeBody
* @return
* @throws FactoryException
*/
public Component getCompositeComponentFromFile(IFile file, boolean includeBody) throws FactoryException {
if (file == null) {
logger.error("File cannot be null");
throw new IllegalArgumentException("File cannot be null");
}
Component component = getComponentFromFile(file);
String compositeComponentFilePath = component.getCompositeMetadataFilePath();
if (Utils.isEmpty(compositeComponentFilePath)) {
throw new FactoryException("Unable to create component - composite filepath for component "
+ component.getFullDisplayName() + " is null or empty");
}
Component compositeComponent = getComponentByFilePath(compositeComponentFilePath);
if (compositeComponent == null) {
throw new FactoryException("Unable to find component composite from path '" + compositeComponentFilePath
+ "'");
}
setCompositeComponentAttributes(compositeComponent, component);
IFile compositeComponentFile = compositeComponent.getFileResource(file.getProject());
if (compositeComponentFile == null || !compositeComponentFile.exists()) {
throw new FactoryException("Unable to find composite file from path '" + compositeComponentFilePath + "'");
}
compositeComponent.setFileResource(compositeComponentFile);
try {
compositeComponent.loadFromFile(includeBody);
} catch (Exception e) {
throw new FactoryException(e);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + component.getFullDisplayName() + " from file '" + file.getName() + "'");
}
return compositeComponent;
}
/**
* Get existing composite component with given associated component. Boolean determines whether the body is
* included.
*
* @param component
* @return
* @throws FactoryException
*/
public Component getCompositeComponentFromComponent(Component component) throws FactoryException {
return getCompositeComponentFromComponent(component, true);
}
/**
* Get existing composite component with given associated component. The component's body will be included.
*
* @param component
* @param includeBody
* @return
* @throws FactoryException
*/
public Component getCompositeComponentFromComponent(Component component, boolean includeBody)
throws FactoryException {
if (component == null
|| (Utils.isEmpty(component.getCompositeMetadataFilePath()) && Utils.isEmpty(component
.getMetadataFileExtension()))) {
logger.error("Component and/or component composite filepath cannot be null");
throw new IllegalArgumentException(
"Component and/or component composite filepath/metadata extension cannot be null");
}
Component compositeComponent = null;
if (Utils.isNotEmpty(component.getCompositeMetadataFilePath())) {
compositeComponent = getComponentByFilePath(component.getCompositeMetadataFilePath());
} else {
compositeComponent = getComponentByExtension(component.getMetadataFileExtension());
}
if (compositeComponent == null) {
throw new FactoryException("Unable to find component composite from path '"
+ component.getCompositeMetadataFilePath() + "'");
}
setCompositeComponentAttributes(compositeComponent, component);
if (component.getFileResource() == null || component.getFileResource().getProject() == null) {
logger.warn("Unable to load existing '" + compositeComponent.getMetadataFilePath()
+ "' from project - associated component's file resource is null. Assuming new.");
return compositeComponent;
}
IFile compositeComponentFile = compositeComponent.getFileResource(component.getFileResource().getProject());
if (compositeComponentFile == null || !compositeComponentFile.exists()) {
logger.warn("Unable to load '" + compositeComponent.getMetadataFilePath()
+ "' from project - composite component file resource is null");
return compositeComponent;
}
compositeComponent.setFileResource(compositeComponentFile);
try {
compositeComponent.loadFromFile(includeBody);
} catch (Exception e) {
throw new FactoryException(e);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + compositeComponent.getFullDisplayName() + " from file '"
+ compositeComponentFile.getName() + "'");
}
return compositeComponent;
}
public MetadataExt getMetadataExt(Component component) throws JAXBException, InstantiationException,
IllegalAccessException {
if (component == null || component.getFileResource() == null || !component.getFileResource().exists()) {
throw new IllegalArgumentException("Component and/or file resource cannot be null and must exist");
}
IFile componentFile = component.getFileResource();
if (logger.isDebugEnabled()) {
logger.debug("Parsing and creating MetadataExt instance '"
+ componentFile.getProjectRelativePath().toPortableString() + "'");
}
MetadataExt metadataExt = component.getDefaultMetadataExtInstance();
Unmarshaller unmarshaller = JAXBContext.newInstance(metadataExt.getClass()).createUnmarshaller();
JAXBElement<? extends MetadataExt> root =
unmarshaller.unmarshal(new StreamSource(componentFile.getRawLocation().toFile()), metadataExt
.getClass());
return root.getValue();
}
private Component getComponentBean(String id) {
Component component = null;
try {
component = (Component) getBean(id);
} catch (Exception e) {
logger.warn("Unable to get component for id '" + id + "': " + e.getMessage());
return null;
}
return component;
}
private void setCompositeComponentAttributes(Component compositeComponent, Component component) {
compositeComponent.setId(component.getId());
compositeComponent.setMetadataComposite(true);
compositeComponent.setFilePath(component.getCompositeMetadataFilePath());
compositeComponent.setName(component.getName());
compositeComponent.setPackageName(component.getPackageName());
compositeComponent.setNamespacePrefix(component.getNamespacePrefix());
compositeComponent.setPackageManifest(false);
compositeComponent.setInstalled(component.isInstalled());
}
public Component createComponent(ProjectPackage projectPackage, String filePath, byte[] file,
FileMetadataExt fileMetadataHandler) throws FactoryException {
// strip package name from filepath, if applicable
String tmpFilePath = filePath;
if (Utils.isNotEmpty(projectPackage.getName()) && filePath.startsWith(projectPackage.getName() + "/")) {
tmpFilePath = filePath.substring(filePath.indexOf("/") + 1);
}
// get object type specific instance of file and set file to new object
Component component = getComponentByFilePath(tmpFilePath);
return loadComponentProperties(projectPackage, component, filePath, file, fileMetadataHandler);
}
private Component loadComponentProperties(ProjectPackage projectPackage, Component component, String filePath,
byte[] file, FileMetadataExt fileMetadataHandler) throws FactoryException {
if (component == null) {
logger.error("Component cannot be null for filepath '" + filePath + "'");
throw new IllegalArgumentException("Component cannot be null for filepath '" + filePath + "'");
}
component.setFilePath(filePath);
component.setPackageName(projectPackage.getName());
component.setFetchTime(Calendar.getInstance().getTimeInMillis());
component.setInstalled(projectPackage.isInstalled());
if (file != null) {
component.setFile(file);
component.setBody(file);
}
if (filePath.endsWith(Constants.DEFAULT_METADATA_FILE_EXTENSION)) {
handleComponentMetadata(projectPackage, component, fileMetadataHandler);
if (logger.isDebugEnabled()) {
logger.debug("Found component metadata file '" + filePath + "'");
}
} else {
setRemoteProperties(component, fileMetadataHandler);
}
component.initChecksum();
if (logger.isDebugEnabled()) {
logger.debug("Created component instance " + component.getFullDisplayName());
}
return component;
}
private void setRemoteProperties(Component component, FileMetadataExt fileMetadataHandler) {
setRemoteProperties(component, component.getMetadataFilePath(), fileMetadataHandler);
}
private void setRemoteProperties(Component component, String filePath, FileMetadataExt fileMetadataHandler) {
if (component == null || fileMetadataHandler == null || Utils.isEmpty(filePath)) {
logger.error("Component, filePath,and/or FileMetadataHandler cannot be null");
throw new IllegalArgumentException("Component, filePath,and/or FileMetadataHandler cannot be null");
}
// set file properties
FileProperties fileProperties = fileMetadataHandler.getFilePropertiesByFilePath(filePath);
if (fileProperties == null) {
logger.warn("Unable to load file properties for '" + filePath + "' - FileProperties is null");
return;
}
component.setFileProperties(fileProperties);
if (logger.isDebugEnabled()) {
logger.debug("Loaded '" + filePath + "' properties into component " + component.getFullDisplayName());
}
}
private void handleComponentMetadata(ProjectPackage projectPackage, Component component,
FileMetadataExt fileMetadataHandler) throws FactoryException {
if (projectPackage == null || component == null || fileMetadataHandler == null) {
logger.error("Component, project package, and/or FileMetadataHandler cannot be null");
throw new IllegalArgumentException("Component, project package, and/or FileMetadataHandler cannot be null");
}
String metadataFilePath = component.getMetadataFilePath();
String compositeFilePath = component.getCompositeMetadataFilePath();
if (Utils.isEmpty(compositeFilePath)) {
logger.warn("Unable to handle metadata component - associated filepath is null");
return;
}
Map<String, byte[]> filePathZipMapping = projectPackage.getFilePathZipMapping();
if (Utils.isEmpty(filePathZipMapping)) {
logger.warn("Filepath zip is null or empty. Skipping.");
return;
}
byte[] file = filePathZipMapping.get(component.getMetadataFilePath());
if (Utils.isEmpty(file)) {
logger.warn("Unable to find metadata file '" + component.getMetadataFilePath() + "' in filepath zip");
return;
}
setRemoteProperties(component, compositeFilePath, fileMetadataHandler);
component.setFilePath(metadataFilePath);
component.setFileName(metadataFilePath);
if (logger.isDebugEnabled()) {
logger.debug("Handled metadata for component " + component.getFullDisplayName());
}
}
private boolean isComponentMetadataMatch(String filePath, IComponent component) {
if (Utils.isEmpty(filePath) || component == null || Utils.isEmpty(component.getFileExtension())) {
return false;
}
boolean hasMetadata = component.isMetadataComposite();
if (hasMetadata && Utils.isEmpty(component.getMetadataFileExtension())) {
logger.warn("Unable to determine component metadata for component type '" + component.getComponentType()
+ "' - metadata file extension is null or empty");
return false;
}
return (hasMetadata && filePath.endsWith(component.getMetadataFileExtension()));
}
private void checkComponentMetadata(String path, Component component) {
if (Utils.isEmpty(path) || component == null) {
return;
}
// check extension for expected metadata part, but folders metatadata, which end in expected part, are not
// considered metadata, but instead an actual component
if (path.endsWith(getMetadataFileExtension()) && !Constants.FOLDER.equals(component.getComponentType())) {
component.setMetadataInstance(true);
if (logger.isDebugEnabled()) {
logger.debug("Set component '" + path + "' as metadata instance");
}
}
}
public boolean hasAssociatedComponentTypes(String componentType) throws FactoryException {
Component component = getComponentByComponentType(componentType);
return Utils.isEmpty(component) ? false : component.hasAssociatedComponentTypes();
}
public List<String> getSubComponentTypes(String componentType) throws FactoryException {
Component component = getComponentByComponentType(componentType);
return Utils.isEmpty(component) ? null : component.getSubComponentTypes();
}
public boolean hasSubComponentTypesForComponentType(String componentType) throws FactoryException {
Component component = getComponentByComponentType(componentType);
return Utils.isEmpty(component) ? false : component.hasSubComponentTypes();
}
public List<String> getSubComponentTypes() {
List<String> componentTypes = getRegisteredComponentTypes();
List<String> childComponentTypes = new ArrayList<String>();
if (Utils.isNotEmpty(componentTypes)) {
Component component = null;
for (String componentType : componentTypes) {
try {
component = getComponentByComponentType(componentType);
if (component != null && component.hasSubComponentTypes()) {
childComponentTypes.addAll(component.getSubComponentTypes());
}
} catch (FactoryException e) {
logger.error("Unable to get child components for component types '" + componentType + "'", e);
}
}
}
return childComponentTypes;
}
public List<String> getParentTypesWithSubComponentTypes() {
List<String> componentTypes = getRegisteredComponentTypes();
List<String> parentComponentTypes = new ArrayList<String>();
if (Utils.isNotEmpty(componentTypes)) {
Component component = null;
for (String componentType : componentTypes) {
try {
component = getComponentByComponentType(componentType);
if (component != null && component.hasSubComponentTypes()) {
parentComponentTypes.add(component.getComponentType());
}
} catch (FactoryException e) {
logger.error("Unable to get child components for component types '" + componentType + "'", e);
}
}
}
return parentComponentTypes;
}
}