/* Copyright 2017 Dmitry Barashev, BarD Software s.r.o This file is part of GanttProject, an opensource project management tool. GanttProject is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GanttProject is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GanttProject. If not, see <http://www.gnu.org/licenses/>. */ package biz.ganttproject.impex.msproject2; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import net.sf.mpxj.FieldType; import net.sf.mpxj.MPXJException; import net.sf.mpxj.ResourceField; import net.sf.mpxj.TaskField; import net.sourceforge.ganttproject.CustomPropertyDefinition; import net.sourceforge.ganttproject.CustomPropertyManager; import net.sourceforge.ganttproject.resource.HumanResourceManager; import net.sourceforge.ganttproject.task.TaskManager; import javax.annotation.Nullable; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.SortedSet; /** * @author dbarashev@bardsoftware.com */ class CustomPropertyMapping { static final String MSPROJECT_TYPE = "MSPROJECT_TYPE"; static Map<CustomPropertyDefinition, FieldType> buildMapping(TaskManager taskManager) throws MPXJException { final SortedSet<TaskField> taskFields = Sets.newTreeSet(new Comparator<TaskField>() { @Override public int compare(TaskField o1, TaskField o2) { return o1.ordinal() - o2.ordinal(); } }); taskFields.addAll(Arrays.asList(TaskField.values())); return buildMapping(taskManager.getCustomPropertyManager(), taskFields, TaskField.class); } static Map<CustomPropertyDefinition, FieldType> buildMapping(HumanResourceManager resourceManager) throws MPXJException { final SortedSet<ResourceField> taskFields = Sets.newTreeSet(new Comparator<ResourceField>() { @Override public int compare(ResourceField o1, ResourceField o2) { return o1.ordinal() - o2.ordinal(); } }); taskFields.addAll(Arrays.asList(ResourceField.values())); return buildMapping(resourceManager.getCustomPropertyManager(), taskFields, ResourceField.class); } private static <T extends Enum<T>, S extends FieldType> Map<CustomPropertyDefinition, FieldType> buildMapping( final CustomPropertyManager customPropertyManager, final SortedSet<S> mpxjFields, final Class<T> enumClass) throws MPXJException { final Map<CustomPropertyDefinition, FieldType> result = new HashMap<>(); class Filter { private LinkedHashSet<CustomPropertyDefinition> allDefs = Sets.newLinkedHashSet(customPropertyManager.getDefinitions()); private void run(final Function<CustomPropertyDefinition, String> fxnFieldName) { run0(new Function<CustomPropertyDefinition, S>() { @Override public S apply(@Nullable CustomPropertyDefinition def) { String msProjectType = fxnFieldName.apply(def); return (msProjectType == null) ? null : (S) Enum.valueOf(enumClass, msProjectType); } }); } private void run0(Function<CustomPropertyDefinition, S> fxnTaskField) { for (Iterator<CustomPropertyDefinition> it = allDefs.iterator(); it.hasNext(); ) { CustomPropertyDefinition def = it.next(); try { FieldType tf = fxnTaskField.apply(def); if (tf != null) { result.put(def, tf); mpxjFields.remove(tf); it.remove(); } } catch (IllegalArgumentException e) { // That's somewhat okay. We have not found such value in the enum, but it might come from the future // versions of MPXJ, so it is not the reason to fail } } } } Filter f = new Filter(); // First pass: search for saved MSPROJECT_TYPE attributes. f.run(new Function<CustomPropertyDefinition, String>() { @Override public String apply(@Nullable CustomPropertyDefinition def) { return def.getAttributes().get(MSPROJECT_TYPE); } }); // Second pass: find properties with predefined names f.run(new Function<CustomPropertyDefinition, String>() { @Override public String apply(@Nullable CustomPropertyDefinition def) { return def.getName().toUpperCase(); } }); // Third pass: provide appropriate types for the remaining properties f.run0(new Function<CustomPropertyDefinition, S>() { @Override public S apply(@Nullable CustomPropertyDefinition def) { String name; switch (def.getPropertyClass()) { case BOOLEAN: name = "FLAG"; break; case INTEGER: case DOUBLE: name = "NUMBER"; break; case TEXT: name = "TEXT"; break; case DATE: name = "DATE"; break; default: assert false : "Should not be here"; name = "TEXT"; } for (int i = 1; i <= 30; i++) { S tf1 = (S)Enum.valueOf(enumClass, name + String.valueOf(i)); if (mpxjFields.contains(tf1)) { return tf1; } } return null; } }); if (!f.allDefs.isEmpty()) { List<String> remainingColumns = Lists.newArrayList(Iterables.transform(f.allDefs, new Function<CustomPropertyDefinition, String>() { @Override public String apply(@Nullable CustomPropertyDefinition def) { return def.getName(); } })); throw new MPXJException(String.format("Some of the custom columns failed to export: %s", remainingColumns)); } return result; } }