package com.metservice.kanban; import static java.lang.String.format; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Reader; import java.util.Arrays; import java.util.Properties; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; import com.metservice.kanban.model.BoardIdentifier; import com.metservice.kanban.model.HtmlColour; import com.metservice.kanban.model.WorkItemType; //TODO This class needs unit tests. /** * Reads and parses a project's .properties file and builds the project * configuration and layout. * * @author Janella Espinas, Chris Cooper */ public class KanbanPropertiesFile { private static final Logger LOGGER = LoggerFactory.getLogger(KanbanPropertiesFile.class); private File file; private Properties properties = new Properties(); public KanbanPropertiesFile(File file) throws IOException { this(new FileReader(file)); this.file = file; } public KanbanPropertiesFile(Reader reader) throws IOException { try { properties.load(reader); } finally { reader.close(); } } private void storeProperties() throws IOException { if (file != null) { FileWriter writer = new FileWriter(file); try { properties.store(writer, ""); } finally { writer.close(); } } } public String[] getWorkItemTypes() throws IOException { return getCommaSeparatedStrings("workItemTypes"); } public String getParentWorkItemType(String workItemType) throws IOException { String propertyKey = format("workItemTypes.%s.parent", workItemType); return getString(propertyKey); } public boolean isChildWorkItemType(String name, String possibleChildName) throws IOException { return getParentWorkItemType(possibleChildName).equals(name); } /** * Returns the phases of the project for the wall. * * @param boardType * @return * @throws IOException */ public String[] getPhaseSequence(BoardIdentifier boardType) throws IOException { String propertyKey = format("boards.%s", boardType.getName()); return getCommaSeparatedStrings(propertyKey); } /** * Returns the phases for a particular workItemType (eg, feature, story). * * @param workItemType * @return * @throws IOException */ public String[] getPhases(String workItemType) throws IOException { String propertyKey = format("workItemTypes.%s.phases", workItemType); return getCommaSeparatedStrings(propertyKey); } /** * Returns the WIP limits for phases * * @param boardType * @return * @throws IOException */ public String[] getPhaseWIPLimit(String workItemType) throws IOException { String propertyKey = format("workItemTypes.%s.wipLimit", workItemType); try { return getCommaSeparatedStrings(propertyKey); } catch (Exception e) { return new String[0]; } } public HtmlColour getWorkItemTypeCardColour(String workItemType) throws IOException { String propertyKey = format("workItemTypes.%s.cardColour", workItemType); return new HtmlColour(getString(propertyKey)); } public HtmlColour getWorkItemTypeBackgroundColour(String workItemType) throws IOException { String propertyKey = format("workItemTypes.%s.backgroundColour", workItemType); return new HtmlColour(getString(propertyKey)); } private String[] getCommaSeparatedStrings(String propertyKey) throws IOException { String commaSeparatedString = getString(propertyKey); return StringUtils.splitPreserveAllTokens(commaSeparatedString, ','); } /** * Gets the value of a given propertyKey from the values in the property * map. * * @param propertyKey * @return * @throws IOException */ String getString(String propertyKey) { String propertyValue = properties.getProperty(propertyKey); // if (propertyValue == null) { // throw new IOException("property \"" + propertyKey + "\" missing from " + file); // } return propertyValue; } public String getContentAsString() throws IOException { BufferedReader in = new BufferedReader(new FileReader(file)); StringBuffer sb = new StringBuffer(); String str; while ((str = in.readLine()) != null) { sb.append(str + "\n"); } in.close(); return sb.toString(); } public void setColumnWipLimit(WorkItemType workItemType, String columnName, Integer wipLimit) throws IOException { LOGGER.info("Setting WIP limit for column {}.{} to {}", new Object[] { workItemType.toString(), columnName, wipLimit }); String[] phases = getPhases(workItemType.toString()); String[] wipLimits = getCommaSeparatedStrings("workItemTypes." + workItemType + ".wipLimit"); if (wipLimits == null) { wipLimits = new String[phases.length]; } if (wipLimits.length < phases.length) { wipLimits = Arrays.copyOf(wipLimits, phases.length); } for (int i = 0; i < phases.length; i++) { if (phases[i].equals(columnName)) { if (wipLimit == null) { wipLimits[i] = ""; } else { wipLimits[i] = wipLimit.toString(); } break; } } String wipLimitsStr = StringUtils.join(wipLimits, ","); properties.put(String.format("workItemTypes.%s.wipLimit", workItemType.getName()), wipLimitsStr); storeProperties(); } public void renameColumn(WorkItemType workItemType, String columnName, String newColumnName) throws IOException { LOGGER.info("Renaming column for WorkItem {} from {} to {} in properties file", new Object[] { workItemType, columnName, newColumnName}); boolean columnFound = false; String[] phases = getCommaSeparatedStrings(String.format("workItemTypes.%s.phases", workItemType.getName())); for (int i = 0; i < phases.length; i++) { if (phases[i].equals(columnName)) { phases[i] = newColumnName; columnFound = true; break; } } Assert.isTrue(columnFound, String.format("Column %s cannot be found in properties file", columnName)); String newPhases = StringUtils.join(phases, ","); properties.put(String.format("workItemTypes.%s.phases", workItemType.getName()), newPhases); String[] boardPhases = getCommaSeparatedStrings("boards.wall"); for (int i = 0; i < boardPhases.length; i++) { if (boardPhases[i].equals(columnName)) { boardPhases[i] = newColumnName; } } String newboardPhases = StringUtils.join(boardPhases, ","); properties.put("boards.wall", newboardPhases); storeProperties(); } }