/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs.layout; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; /** * Generic class used by AddressTemplate and NameTemplate layouts * @since 1.12 */ public abstract class LayoutTemplate { protected static final String LAYOUT_TOKEN = "<!-- openmrsToken -->"; protected String displayName; protected String codeName; protected String country; protected Map<String, String> nameMappings; protected Map<String, String> sizeMappings; protected Map<String, String> elementDefaults; protected Map<String, String> elementRegex; protected Map<String, String> elementRegexFormats; protected List<String> lineByLineFormat; protected List<String> requiredElements; // The largest number of tokens on one given line protected int maxTokens = 0; protected String startDate; protected String endDate; public LayoutTemplate() { } /** * Very crude way of setting just one line of template. This just puts * something on {@link #setLineByLineFormat(List)} with this string * * @param simpleTemplate * first template line */ public LayoutTemplate(String simpleTemplate) { setLineByLineFormat(Arrays.asList(simpleTemplate)); } public abstract String getLayoutToken(); public abstract String getNonLayoutToken(); private String replaceTokens(String line) { LayoutSupport<?> as = getLayoutSupportInstance(); List<String> specialTokens = nonUniqueStringsGoLast(as.getSpecialTokens()); for (String token : specialTokens) { line = line.replaceAll(token, LAYOUT_TOKEN); } return line; } private List<Map<String, String>> convertToTokens(String line, String[] nonTokens) { List<Map<String, String>> ret = null; if (line != null && nonTokens != null && nonTokens.length > 0) { int idxCurr = -1; for (int i = 0; i < nonTokens.length; i++) { String nonToken = nonTokens[i]; if (idxCurr + 1 < line.length()) { idxCurr = line.indexOf(nonToken, idxCurr + 1); } if (ret == null) { ret = new Vector<Map<String, String>>(); } if (i == 0 && idxCurr > 0) { // this means there is a token at the beginning - we'll have to grab it Map<String, String> currToken = new HashMap<String, String>(); currToken.put("isToken", getLayoutToken()); String realToken = line.substring(0, idxCurr); currToken.put("displayText", this.getNameMappings().get(realToken)); currToken.put("displaySize", this.getSizeMappings().get(realToken)); currToken.put("codeName", realToken); //numTokens++; ret.add(currToken); } if (i < nonTokens.length - 1) { // this means we are still not at the last non-token, so let's add this non-token AND this token int idxNext = line.indexOf(nonTokens[i + 1], idxCurr + 1); Map<String, String> currNonToken = new HashMap<String, String>(); currNonToken.put("isToken", getNonLayoutToken()); currNonToken.put("displayText", nonToken); Map<String, String> currToken = new HashMap<String, String>(); currToken.put("isToken", getLayoutToken()); //HERE: real Token is wrong... String realToken = line.substring(idxCurr + nonToken.length(), idxNext); currToken.put("displayText", this.getNameMappings().get(realToken)); currToken.put("displaySize", this.getSizeMappings().get(realToken)); currToken.put("codeName", realToken); //numTokens++; ret.add(currNonToken); ret.add(currToken); } else { // we are on the last non-token, so check if it is the end Map<String, String> currNonToken = new HashMap<String, String>(); currNonToken.put("isToken", getNonLayoutToken()); currNonToken.put("displayText", nonToken); ret.add(currNonToken); if (idxCurr + nonToken.length() >= line.length()) { // we are at the end, so we are done } else { // we need to add one last token at the end Map<String, String> currToken = new HashMap<String, String>(); currToken.put("isToken", getLayoutToken()); String realToken = line.substring(idxCurr + nonToken.length()); currToken.put("displayText", this.getNameMappings().get(realToken)); currToken.put("displaySize", this.getSizeMappings().get(realToken)); currToken.put("codeName", realToken); //numTokens++; ret.add(currToken); } } } } else if (line != null && line.length() > 0) { // looks like we have a single token on a line by itself if (ret == null) { ret = new Vector<Map<String, String>>(); } Map<String, String> currToken = new HashMap<String, String>(); // adding a nontoken to match the code that does "more than a single token on a line" Map<String, String> currNonToken = new HashMap<String, String>(); currNonToken.put("isToken", getNonLayoutToken()); currNonToken.put("displayText", ""); ret.add(currNonToken); currToken.put("isToken", getLayoutToken()); String realToken = line; currToken.put("displayText", this.getNameMappings().get(realToken)); currToken.put("displaySize", this.getSizeMappings().get(realToken)); currToken.put("codeName", realToken); //numTokens++; ret.add(currToken); } if (ret != null && this.maxTokens < ret.size()) { this.maxTokens = ret.size(); } return ret; } public List<List<Map<String, String>>> getLines() { List<List<Map<String, String>>> ret = null; if (this.lineByLineFormat != null) { for (String line : this.lineByLineFormat) { if (ret == null) { ret = new Vector<List<Map<String, String>>>(); } String tokenizedLine = replaceTokens(line); String[] nonTokens = tokenizedLine.split(LAYOUT_TOKEN); List<Map<String, String>> lineTokens = convertToTokens(line, nonTokens); ret.add(lineTokens); } return ret; } else { return ret; } } /** * @return the codeName */ public String getCodeName() { return codeName; } /** * @param codeName the codeName to set */ public void setCodeName(String codeName) { this.codeName = codeName; } /** * @return the country */ public String getCountry() { return country; } /** * @param country the country to set */ public void setCountry(String country) { this.country = country; } /** * @return the displayName */ public String getDisplayName() { return displayName; } /** * @param displayName the displayName to set */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * @return the elementDefaults */ public Map<String, String> getElementDefaults() { return elementDefaults; } /** * @param elementDefaults the elementDefaults to set */ public void setElementDefaults(Map<String, String> elementDefaults) { this.elementDefaults = elementDefaults; } /** * Get the element regular expressions. These can be used to enforce that an element matches a * regex. * * @return the elementRegex */ public Map<String, String> getElementRegex() { return elementRegex; } /** * Set the element regular expressions. These can be used to enforce that an element matches a * regex. * * @param elementRegex the elementRegex to set */ public void setElementRegex(Map<String, String> elementRegex) { this.elementRegex = elementRegex; } /** * Get the element formats. These can be used to display an example format that an element * should look like. * * @return the elementFormats */ public Map<String, String> getElementRegexFormats() { return elementRegexFormats; } /** * Set the element formats. These can be used to display an example format that an element * should look like. * * @param elementRegexFormats the elementFormats to set */ public void setElementRegexFormats(Map<String, String> elementRegexFormats) { this.elementRegexFormats = elementRegexFormats; } /** * @return the lineByLineFormat */ public List<String> getLineByLineFormat() { return lineByLineFormat; } /** * @param lineByLineFormat the lineByLineFormat to set */ public void setLineByLineFormat(List<String> lineByLineFormat) { this.lineByLineFormat = lineByLineFormat; } /** * @return the requiredElements */ public List<String> getRequiredElements() { return requiredElements; } /** * @param requiredElements the requiredElements to set */ public void setRequiredElements(List<String> requiredElements) { this.requiredElements = requiredElements; } /** * @return the maxTokens */ public int getMaxTokens() { if (maxTokens == -1) { // initialize the maxTokens variable getLines(); } return maxTokens; } /** * @param maxTokens the maxTokens to set */ public void setMaxTokens(int maxTokens) { this.maxTokens = maxTokens; } /** * @return the nameMappings */ public Map<String, String> getNameMappings() { return nameMappings; } /** * @param nameMappings the nameMappings to set */ public void setNameMappings(Map<String, String> nameMappings) { this.nameMappings = nameMappings; } /** * @return the sizeMappings */ public Map<String, String> getSizeMappings() { return sizeMappings; } /** * @param sizeMappings the sizeMappings to set */ public void setSizeMappings(Map<String, String> sizeMappings) { this.sizeMappings = sizeMappings; } public abstract LayoutSupport<?> getLayoutSupportInstance(); public List<String> nonUniqueStringsGoLast(List<String> strListArg) { List<String> dup = new ArrayList<String>(); // copy the list so we don't get concurrentmodification exceptions List<String> strList = new ArrayList(strListArg); for (String s : strList) { for (String sInner : strList) { if (sInner.indexOf(s) != -1 && s.length() < sInner.length() && !dup.contains(s)) { dup.add(s); } } } if (dup.size() > 1) { dup = nonUniqueStringsGoLast(dup); } strList.removeAll(dup); strList.addAll(dup); return strList; } }