/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.components.geocode.postcodes.impl; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.opendoorlogistics.components.geocode.postcodes.impl.PCRecord.StrField; import com.opendoorlogistics.core.gis.postcodes.UKPostcodes; import com.opendoorlogistics.core.gis.postcodes.UKPostcodes.UKPostcodeLevel; import com.opendoorlogistics.core.utils.strings.Strings; final public class CountryConfigs { private enum HierarchyType{ NONE(1), TWO_LEVEL_HYPHEN(2), TWO_LEVEL_SPACE(2), GB(4); private HierarchyType(int nbLevels){ this.nbLevels= nbLevels; } private final int nbLevels; int nbLevels(){ return nbLevels; } } public static interface CountryProcessor{ PCRecord processRecord(PCRecord record); /** * Split the postcode supplied by geonames into different * hierarchical levels. * @param pc * @param returnPartial * @return Null if entry not understood what-so-ever OR a list of size nbLevels() * where some entries might be null (e.g. the most detailed geographic level) * if only higher levels are recognised and returnPartial is true */ List<String> splitByLevels(String pc, boolean returnPartial); int nbLevels(); String standardisePostcode(String pc); } private static class DefaultProcessor implements CountryProcessor{ private final HierarchyType hierarchyType; public DefaultProcessor( HierarchyType hierarchyType) { super(); this.hierarchyType = hierarchyType; } @Override public PCRecord processRecord(PCRecord record) { return record; } @Override public List<String> splitByLevels(String pc, boolean returnPartial) { switch (hierarchyType) { case TWO_LEVEL_HYPHEN: return twoLevelSplit("-",pc, returnPartial); case TWO_LEVEL_SPACE: return twoLevelSplit(" ",pc, returnPartial); default: ArrayList<String> ret = new ArrayList<>(); ret.add(pc); return ret; } } private List<String> twoLevelSplit(String separator, String pc, boolean returnPartial) { ArrayList<String> ret = new ArrayList<>(); pc = Strings.std(pc); String [] split = pc.split(separator); if(split.length==2){ ret.add(pc); ret.add(0,split[0]); return ret; } // There is no point returning a partial as we have no way of knowing // which part of the postcode hierarchy the input belongs to return null; } @Override public int nbLevels(){ return hierarchyType.nbLevels(); } @Override public String standardisePostcode(String pc) { return Strings.std(pc).toUpperCase(); } } private static final CountryProcessor defaultProcessor = new DefaultProcessor(HierarchyType.NONE); private static final CountryProcessor twoLevelHyphen = new DefaultProcessor(HierarchyType.TWO_LEVEL_HYPHEN); private static final CountryProcessor twoLevelSpace = new DefaultProcessor(HierarchyType.TWO_LEVEL_SPACE); private static final CountryProcessor france = new DefaultProcessor(HierarchyType.NONE) { private Pattern getNumber = Pattern.compile("\\D*(\\d+).*"); @Override public PCRecord processRecord(PCRecord record) { String pc = record.getField(StrField.POSTAL_CODE); pc = Strings.std(pc); Matcher matcher = getNumber.matcher(pc); if(matcher.matches()==false){ return null; } pc = matcher.group(1); record.setField(StrField.POSTAL_CODE, pc); return record; } }; public static class GBProcessor extends DefaultProcessor{ private final boolean removeExtraDistrictLetter; public GBProcessor(boolean removeExtraDistrictLetter ) { super(HierarchyType.GB); this.removeExtraDistrictLetter = removeExtraDistrictLetter; } @Override public List<String> splitByLevels(String pc, boolean returnPartial) { pc = standardisePostcode(pc); UKPostcodeLevel level = identifyLevel(pc); if(level==null || (level.compareTo(UKPostcodeLevel.Unit)<0 && returnPartial==false)){ return null; } ArrayList<String> ret = new ArrayList<>(UKPostcodeLevel.values().length); for(UKPostcodeLevel fetchLevel : UKPostcodeLevel.values()){ String fetched=null; if(fetchLevel.compareTo(level)<0){ Pattern pattern=null; switch(fetchLevel){ case Area: pattern = UKPostcodes.areaFromAnyLevelPC; break; case District: pattern = UKPostcodes.districtFromAnyLevelPC; break; case Sector: pattern = UKPostcodes.sectorFromUnit; break; default: throw new RuntimeException(); } Matcher matcher = pattern.matcher(pc); if(!matcher.matches()){ throw new RuntimeException(); } fetched = matcher.group(1); } else if(fetchLevel==level){ fetched = pc; } ret.add(fetched); } return ret; } /** * Identify the level for a postcode that's already UK-standardised. * @param standardisedPc * @return */ public UKPostcodeLevel identifyLevel(String standardisedPc){ standardisedPc = standardisePostcode(standardisedPc); if(UKPostcodes.isUnit.matcher(standardisedPc).find()){ return UKPostcodeLevel.Unit; } if(UKPostcodes.isSector.matcher(standardisedPc).find()){ return UKPostcodeLevel.Sector; } if(UKPostcodes.isDistrict.matcher(standardisedPc).find()){ return UKPostcodeLevel.District; } if(UKPostcodes.isArea.matcher(standardisedPc).find()){ return UKPostcodeLevel.Area; } return null; } /** * Standardise a full or partial UK postcode * @param s */ @Override public String standardisePostcode(String s){ return UKPostcodes.standardisePostcode(s, removeExtraDistrictLetter); } } public static void main(String[] args) { // Pattern getNumber = Pattern.compile("^([a-z][a-z]?).*"); GBProcessor gb = new GBProcessor(true); System.out.println(gb.splitByLevels("E1W 1AA", false)); // Pattern pattern = gb.areaFromAnyLevelStdPC; // String s = "ab10 1aa"; // int hash = s.hashCode(); // System.out.println(hash); // Matcher matcher = pattern.matcher(s); // if(matcher.matches()){ // System.out.println(matcher.group(1)); // }else{ // System.out.println("no match"); // } } private static final GBProcessor gb = new GBProcessor(true); public static CountryProcessor getProcessor(String countryCode){ countryCode = Strings.std(countryCode); switch(countryCode){ case "br": return twoLevelHyphen; case "cz": return twoLevelSpace; case "fr": return france; case "jp": return twoLevelHyphen; case "pl": return twoLevelHyphen; case "pt": return twoLevelHyphen; case "sk": return twoLevelSpace; case "gb": return gb; default: return defaultProcessor; } } }