SlovakStemmer.java

package sk.iway.iwcm.system.fulltext.lucene;

/**
 *  SlovakStemmer.java
 *
 *@Title        webjet7
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2012
 *@author       $Author: jeeff thaber $
 *@version      $Revision: 1.3 $
 *@created      Date: 29.5.2012 14:19:04
 *@modified     $Date: 2004/08/16 06:26:11 $
 */
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class SlovakStemmer {


	private static List<String []> pripony = vytvorPripony();

	private static String [] ei = new String[] {"e", "i", "iam", "iach", "iami", "í", "ia", "ie", "iu", "ím"};

	private static HashMap<String, String> dtnl = vytvorDTNL();

	private static HashMap<String, String> dlheKratke = vytvorDlheKratke();

	private static String [] cudzieSlovaPredIa = new String[] {"c", "z", "g"};

	private static String [] samohlasky = new String[] {"a", "á", "ä", "e", "é",
		"i", "í", "o", "ó", "u", "ú", "y", "ý", "ô", "ia", "ie", "iu"};

	private static String [] lr = new String [] {"r", "ŕ", "l", "ĺ"};

	protected SlovakStemmer() {
		//utility class
	}

	/**
	 * Ostemuje zadane slovo.
	 *
	 * @param in slovo
	 * @return jeho ostemovany tvar
	 */
	public static String stem(String in) {

		return zbavSaPripon(in);

	}




	private static String zbavSaPripon(String in) {

		for(String [] prip : pripony) {

			for(String pripona : prip) {

				//nasli sme priponu
				if(in.endsWith(pripona)) {

					//detenele, ditinili
					if(ei(pripona)) {
						return zmenDTNL(odstranPriponu(in, pripona));
					}

					//cudzie -cia, -gia...
					if(pripona.startsWith("i")) {
						if(cudzie(in, pripona)) {
							return odstranPriponu(in, pripona).concat("i");
						}
					}

					//ci nepride k overstemmingu
					if(overstemming(in, pripona))
						return in;

					//inak odstranime priponu
					return odstranPriponu(in, pripona);
				}
			}
		}

		//konci na er -> peter, sveter....
		if(in.endsWith("er")) {
			return (odstranPriponu(in, "er")).concat("r");
		}

		//konci na ok -> sviatok, odpadok....
		if(in.endsWith("ok")) {
			return (odstranPriponu(in, "ok")).concat("k");
		}

		//konci na zen -> podobizen, bielizen....
		if(in.endsWith("zeň")) {
			return (odstranPriponu(in, "eň")).concat("n");
		}

		//konci na ol -> kotol....
		if(in.endsWith("ol")) {
			return (odstranPriponu(in, "ol")).concat("l");
		}

		//konci na ic -> matematic (matematik, matematici)... (pracovnici vs slnecnic)
		if(in.endsWith("ic")) {
			return (odstranPriponu(in, "c")).concat("k");
		}

		//konci na ec -> tanec, obec....
		if(in.endsWith("ec")) {
			return (odstranPriponu(in, "ec")).concat("c");
		}

		//konci na um -> studium, stadium....
		if(in.endsWith("um")) {
			return (odstranPriponu(in, "um"));
		}

		//genitiv pluralu pre vzory zena, ulica, gazdina, mesto, srdce ???
		return poriesGenitivPluralu(in);

	}


	//problem = napriklad pes - psa, den - dna a podobne TODO
	private static boolean overstemming(String in, String pripona) {

		//overstemming zrejme vtedy, ked nam ostane koren bez samohlasky / bez l/r v strede slova

		String s = odstranPriponu(in, pripona);

		for(String samohlaska : samohlasky) {
			if(s.contains(samohlaska))
				return false;
		}

		for(String rl : lr) {
			if(s.contains(rl) && !s.endsWith(rl))
				return false;
		}

		return true;
	}



	//problem = ako rozoznat, ci je to zensky/stredny rod. pr: lama - lam vs. pan - panov TODO




	/**
	 *
	 * @param in
	 * @return ak je to genitiv pluralu, vrat spravny tvar, ak nie je, vrat in
	 */
	private static String poriesGenitivPluralu(String in) {

		//v poslednej slabike musi byt dlha samohlaska / dlhe r/l

		for(Map.Entry<String, String> entry : dlheKratke.entrySet()) {
			String dlha = entry.getKey();

			if(in.contains(dlha)) {

				if(poslednaSlabika(in, dlha)) {

					in = nahradPosledne(in, dlha, entry.getValue());

					break;
				}
			}

		}

		return in;
	}


	/**
	 * posledna slabika - ak sa za danym substringom uz nenaxadza uz ziadna samohlaska
	 * @param s string
	 * @param t substring
	 * @return
	 */
	private static boolean poslednaSlabika(String s, String t) {

		int pokial = s.lastIndexOf(t);
		String koniec = s.substring(pokial);
		koniec = koniec.substring(t.length());

		for(String samohlaska : samohlasky) {

			if(koniec.contains(samohlaska))
				return false;

		}

		return true;
	}







	/**
	 * nahradi posledny vyskyt podretazca v retazci inym podretazcom
	 * @param s
	 * @param co
	 * @param cim
	 * @return
	 */
	private static String nahradPosledne(String s, String co, String cim) {

		int pokial = s.lastIndexOf(co);

		String koniec = s.substring(pokial);

		koniec = koniec.substring(co.length());

		koniec = cim + koniec;

		s = s.substring(0, pokial) + koniec;

		return s;
	}



	//problem - srdcia TODO
	private static boolean cudzie(String in, String pripona) {
		String s = odstranPriponu(in, pripona);

		for(String koncovka : cudzieSlovaPredIa) {
			if(s.endsWith(koncovka))
				return true;
		}

		return false;
	}




	private static String odstranPriponu(String s, String p) {

		if(!s.endsWith(p))
			return s;

		return s.substring(0, s.length() - p.length());
	}

	//TODO problem - napr sused - sudedia vs. priatel - priatelia
	private static boolean ei(String s1) {
		for(String s2 : ei) {
			if(s1.equals(s2))
				return true;
		}
		return false;
	}


	private static String zmenDTNL(String in) {

		for(Map.Entry<String, String> entry : dtnl.entrySet()) {
			String tvrdy = entry.getKey();
			if(in.endsWith(tvrdy)) {
				in = in.substring(0, in.length() - 1);
				in = in.concat(entry.getValue());
			}
		}

		return in;
	}






	/**
	 * Vytvori zoznam pripon podstatnych mien od najdlhsich po najkratsie tak,
	 * 	ze ak je nejaka pripona obsiahnuta v inej, je kratsia.
	 *
	 * Pripony pre vzory:
	 * 		chlap, hrdina, dub, stroj, hostinsky
	 * 		zena, ulica, dlan, kost, gazdina
	 * 		mesto, srdce, vysvedcenie, dievca (+ holuba)
	 *
	 * @return zoznam pripon
	 */
	private static List<String[]> vytvorPripony() {

		List<String[]> p = new ArrayList<>();

		// od najdlhsich po najkratsie tak, ze ak je nejaka pripona obsiahnuta v inej, je kratsia

		//najdlhsie (nie su v ziadnej inej obsiahnute)
		p.add(new String[] {"encami", "atami", "ätami", "iami", "ými", "ovi", "ati", "äti",
				"eniec", "ence", "ie", "aťom", "äťom", "encom", "atám", "ätám", "iam", "ím",
				"ým", "encoch", "atách", "ätách", "iach", "ých", "aťa", "äťa", "ovia", "atá",
				"ätá", "aťu", "äťu", "ému", "iu", "iou", "ov", "at", "ät", "ä", "ého", "ý",
				"y", "ií", "ej", "ú", "é"});

		p.add(new String[] {"e", "om", "ami", "ám", "och", "ach", "ách", "ia", "á", "ou", "o", "ii", "í"});

		p.add(new String[] {"mi", "a", "u"});

		p.add(new String[] {"i"});

		return p;

	}


	/**
	 * vramcizenskeho a stredneho rodu - ideme riesit genitiv pluralu
	 * @return
	 */
	private static HashMap<String, String> vytvorDlheKratke() {

		HashMap<String, String> h = new HashMap<>();

		h.put("á", "a");
		h.put("ie", "e");
		h.put("ŕ", "r");
		h.put("ĺ", "l");
		h.put("í", "i");
		h.put("ú", "u");
		h.put("ô", "o");

		return h;
	}



	/**
	 * d -> ď, t -> ť, ...
	 * @return
	 */
	private static HashMap<String, String> vytvorDTNL() {

		HashMap<String, String> h = new HashMap<>();

		h.put("d", "ď");
		h.put("t", "ť");
		h.put("n", "ň");
		h.put("l", "ľ");

		return h;
	}






}