/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * 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: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.project.server; import org.eclipse.che.api.core.NotFoundException; import org.eclipse.che.api.core.model.project.type.Attribute; import org.eclipse.che.api.project.server.RegisteredProject.Problem; import org.eclipse.che.api.project.server.type.ProjectTypeDef; import org.eclipse.che.api.project.server.type.ProjectTypeRegistry; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static com.google.common.collect.Lists.newArrayList; import static java.lang.String.format; import static org.eclipse.che.api.core.ErrorCodes.ATTRIBUTE_NAME_PROBLEM; import static org.eclipse.che.api.core.ErrorCodes.PROJECT_TYPE_IS_NOT_REGISTERED; /** * @author gazarenkov */ public class ProjectTypes { private final String projectPath; private final ProjectTypeRegistry projectTypeRegistry; private ProjectTypeDef primary; private final Map<String, ProjectTypeDef> mixins; private final Map<String, ProjectTypeDef> all; private final Map<String, Attribute> attributeDefs; private final List<Problem> problems; ProjectTypes(String projectPath, String type, List<String> mixinTypes, ProjectTypeRegistry projectTypeRegistry, List<Problem> problems) { mixins = new HashMap<>(); all = new HashMap<>(); attributeDefs = new HashMap<>(); this.problems = problems != null ? problems : newArrayList(); this.projectTypeRegistry = projectTypeRegistry; this.projectPath = projectPath; ProjectTypeDef tmpPrimary; if (type == null) { this.problems.add(new Problem(PROJECT_TYPE_IS_NOT_REGISTERED, "No primary type defined for " + projectPath + " Base Project Type assigned.")); tmpPrimary = ProjectTypeRegistry.BASE_TYPE; } else { try { tmpPrimary = projectTypeRegistry.getProjectType(type); } catch (NotFoundException e) { this.problems.add(new Problem(PROJECT_TYPE_IS_NOT_REGISTERED, "Primary type " + type + " defined for " + projectPath + " is not registered. Base Project Type assigned.")); tmpPrimary = ProjectTypeRegistry.BASE_TYPE; } if (!tmpPrimary.isPrimaryable()) { this.problems.add(new Problem(PROJECT_TYPE_IS_NOT_REGISTERED, "Project type " + tmpPrimary.getId() + " is not allowable to be primary type. Base Project Type assigned.")); tmpPrimary = ProjectTypeRegistry.BASE_TYPE; } } this.primary = tmpPrimary; all.put(primary.getId(), primary); List<String> mixinsFromConfig = mixinTypes; if (mixinsFromConfig == null) { mixinsFromConfig = new ArrayList<>(); } for (Attribute attr : primary.getAttributes()) { attributeDefs.put(attr.getName(), attr); } for (String mixinFromConfig : mixinsFromConfig) { if (mixinFromConfig.equals(primary.getId())) { continue; } final ProjectTypeDef mixin; try { mixin = projectTypeRegistry.getProjectType(mixinFromConfig); } catch (NotFoundException e) { this.problems.add(new Problem(PROJECT_TYPE_IS_NOT_REGISTERED, "Project type " + mixinFromConfig + " is not registered. Skipped.")); continue; } if (!mixin.isMixable()) { this.problems.add(new Problem(PROJECT_TYPE_IS_NOT_REGISTERED, "Project type " + mixin + " is not allowable to be mixin. It not mixable. Skipped.")); continue; } if (!mixin.isPersisted()) { continue; } // detect duplicated attributes for (Attribute attr : mixin.getAttributes()) { final String attrName = attr.getName(); final Attribute attribute = attributeDefs.get(attrName); if (attribute != null && !attribute.getProjectType().equals(attr.getProjectType())) { this.problems.add(new Problem(ATTRIBUTE_NAME_PROBLEM, format("Attribute name conflict. Duplicated attributes detected for %s. " + "Attribute %s declared in %s already declared in %s. Skipped.", projectPath, attrName, mixin.getId(), attribute.getProjectType()))); continue; } attributeDefs.put(attrName, attr); } // Silently remove repeated items from mixins if any mixins.put(mixinFromConfig, mixin); all.put(mixinFromConfig, mixin); } } public Map<String, Attribute> getAttributeDefs() { return attributeDefs; } public ProjectTypeDef getPrimary() { return primary; } public Map<String, ProjectTypeDef> getMixins() { return mixins; } public Map<String, ProjectTypeDef> getAll() { return all; } /** * Reset project types and atrributes after initialization * in case when some attributes are not valid * (for instance required attributes are not initialized) * * @param attributesToDel - invalid attributes */ void reset(Set<Attribute> attributesToDel) { Set<String> ptsToDel = new HashSet<>(); for (Attribute attr : attributesToDel) { ptsToDel.add(attr.getProjectType()); } Set<String> attrNamesToDel = new HashSet<>(); for (String pt : ptsToDel) { ProjectTypeDef typeDef = all.get(pt); for (Attribute attrDef : typeDef.getAttributes()) { attrNamesToDel.add(attrDef.getName()); } } // remove project types for (String typeId : ptsToDel) { this.all.remove(typeId); if (this.primary.getId().equals(typeId)) { this.primary = ProjectTypeRegistry.BASE_TYPE; this.all.put(ProjectTypeRegistry.BASE_TYPE.getId(), ProjectTypeRegistry.BASE_TYPE); } else { mixins.remove(typeId); } } // remove attributes for (String attr : attrNamesToDel) { this.attributeDefs.remove(attr); } } void addTransient(FolderEntry projectFolder) { for (ProjectTypeDef pt : projectTypeRegistry.getProjectTypes()) { // NOTE: Only mixable types allowed if (pt.isMixable() && !pt.isPersisted() && pt.resolveSources(projectFolder).matched()) { all.put(pt.getId(), pt); mixins.put(pt.getId(), pt); for (Attribute attr : pt.getAttributes()) { final String attrName = attr.getName(); final Attribute attribute = attributeDefs.get(attr.getName()); // If attr from mixin is going to be added but we already have some attribute with the same name, // check whether it's the same attribute that comes from the common parent PT, e.g. from Base PT. if (attribute != null && !attribute.getProjectType().equals(attr.getProjectType())) { problems.add(new Problem(ATTRIBUTE_NAME_PROBLEM, format("Attribute name conflict. Duplicated attributes detected for %s. " + "Attribute %s declared in %s already declared in %s. Skipped.", projectPath, attrName, pt.getId(), attribute.getProjectType()))); continue; } attributeDefs.put(attrName, attr); } } } } }