Prop.java

package sk.iway.iwcm.i18n;

import org.apache.commons.lang.ArrayUtils;
import org.apache.struts.util.ResponseUtils;
import sk.iway.iwcm.*;
import sk.iway.iwcm.components.translation_keys.jpa.MissingKeysDto;
import sk.iway.iwcm.database.ComplexQuery;
import sk.iway.iwcm.database.Mapper;
import sk.iway.iwcm.doc.DocDB;
import sk.iway.iwcm.doc.DocDetails;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.system.ConfDB;
import sk.iway.iwcm.system.cluster.ClusterDB;
import sk.iway.iwcm.system.multidomain.MultiDomainFilter;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  Description of the Class
 *
 *@Title        magma-web
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2002
 *@author       $Author: jeeff $
 *@version      $Revision: 1.8 $
 *@created      Streda, 2002, j�l 17
 *@modified     $Date: 2004/03/18 15:44:59 $
 */
public class Prop
{
	public static final boolean DEFAULT_TEXTS = true;

	public static final boolean CURRENT_TEXTS = false;

	/**
	 *  Description of the Field
	 */
	private static final String CONTEXT_I18N_PROP_HASHTABLE = "context_i18n_prop_hashtable";
	/**
	 *  Description of the Field
	 */
	public static final String SESSION_I18N_PROP_LNG = "session_i18n_prop_lng";

	private IwayProperties properties;

	//pre interne ucely instancie, pretoze po vytvoreni instancie tato uz nemala informaciu o jazyku !!
	private final String language;

	private static ConcurrentHashMap<String, Set<MissingKeysDto>> missingTexts = new ConcurrentHashMap<>();

	private static long lastUpdate;

	private long created = System.currentTimeMillis();

	private Prop(IwayProperties properties, String lng)
	{
		this.properties = properties;
		language = lng;
	}

	@SuppressWarnings("unchecked")
	private static Map<String, Prop> getPropHashtabl(ServletContext context)
	{
		Map<String, Prop> propHashtable = null;
		if (context != null) propHashtable = (HashMap<String, Prop>)context.getAttribute(CONTEXT_I18N_PROP_HASHTABLE);
		if (propHashtable == null)
		{
			propHashtable = new HashMap<>();
			if (context!=null) context.setAttribute(CONTEXT_I18N_PROP_HASHTABLE, propHashtable);
		}
		return propHashtable;
	}

	private static Prop getPropFromHashtable(String lng)
	{
		return getPropFromHashtable(lng, Constants.getServletContext());
	}

	private static Prop getPropFromHashtable(String lng, ServletContext servletContext)
	{
		Map<String, Prop> propHashtable = getPropHashtabl(servletContext);
		if (Tools.isEmpty(lng))
		{
			lng = "sk";
		}
		Prop p = propHashtable.get(lng);
		if (p == null)
		{
			if ("ru".equals(lng))
			{
				p = new Prop(new IwayProperties("utf-8"),lng);
			}
			else if ("sk".equals(lng))
			{
				p = new Prop(new IwayProperties("utf-8"),lng);
			}
			else
			{
				p = new Prop(new IwayProperties(),lng);
			}
			propHashtable.put(lng, p);
		}
		return(p);
	}

	/**
	 * @Deprecated neviem na co toto sluzi, nikde sa nepouziva
	 */
	public Prop(ServletContext servletContext, boolean defaults, String lng)
	{
		Logger.println(this,"reading resources ["+Constants.getInstallName()+"]");

		properties = new IwayProperties();

		loadLanguageProp("sk", toFileNamePart("sk"), !defaults, servletContext);
		language = lng;
		ClusterDB.addRefresh(Prop.class);
	}

	private static String toFileNamePart(String lng)
	{
		if (Tools.isEmpty(lng)) lng="sk";
		return "sk".equals(lng) ? "" : "_"+lng;
	}

	private synchronized void loadLanguageProp(String languageKey, String fileLng, boolean loadCustomizedTexts, ServletContext servletContext)
	{
		if (Tools.isEmpty(languageKey)) languageKey = "sk";

		if (getPropFromHashtable(languageKey, servletContext).getProperties().isEmpty()==false)
			return;
		try
		{
			properties = getDefaulProperties(languageKey, fileLng, servletContext);
			if (loadCustomizedTexts && InitServlet.isWebjetConfigured())
			{
				new ComplexQuery().
						setSql("SELECT * FROM " + ConfDB.PROPERTIES_TABLE_NAME + " WHERE lng = ? ORDER BY update_date ASC").
						setParams(languageKey).
						list(new Mapper<Void>(){
							@Override
							public Void map(ResultSet rs) throws SQLException{
								String key = DB.getDbString(rs, "prop_key");
								String value = DB.getDbString(rs, "prop_value");
								if (key != null) key = key.trim();
								properties.setProperty(key, value);
								return null;
							}
						});
			}
		}
		catch (Exception e)
		{
			Logger.error(Prop.class, e.getMessage());
		}
	}


	public static Prop getInstance(HttpServletRequest request)
	{
		return(getInstance(Constants.getServletContext(), request));
	}

	/**
	 *  Gets the text attribute of the Prop class
	 *
	 *@param  servletContext  Description of the Parameter
	 *@param  request         Description of the Parameter
	 *@return                 The text value
	 */
	public static Prop getInstance(ServletContext servletContext, HttpServletRequest request)
	{
		String lng = getLng(request, false);
		boolean refresh = false;

		return(getInstance(servletContext, lng, refresh));
	}

	public static Prop getInstance()
	{
		RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
		String lng = Constants.getString("defaultLanguage");
		if (rb != null) lng = rb.getLng();
		return getInstance(Constants.getServletContext(), lng, false);
	}

	public static String getLng(HttpSession session)
	{
		String lng = (String)session.getAttribute(SESSION_I18N_PROP_LNG);
		if (lng==null)
			lng = (String)session.getAttribute("lng");
		if (lng == null)
			lng = Constants.getString("defaultLanguage");
		return lng;
	}

	public static String getLng(HttpServletRequest request, boolean noParameter)
	{
		if (request == null) return Constants.getString("defaultLanguage");
		HttpSession session = request.getSession();
		String lng = null;
		if (noParameter == false) lng = ResponseUtils.filter(request.getParameter("language"));
		else if (request.getParameter("__lng")!=null)
		{
			lng = request.getParameter("__lng");
		}
		else if (request.getParameter("lng")!=null)
		{
			lng = request.getParameter("lng");
		}
		if (lng == null)
		{
			//PageLng
			lng = (String)request.getAttribute("PageLng");
			//Logger.debug(Prop.class, "getInstance1 lng="+lng);
		}
		if (lng==null)
		{
			//najskor musi ist test pre admina, aby sa nemenil jazyk admin casti
			lng = (String)session.getAttribute(SESSION_I18N_PROP_LNG);
			//Logger.debug(Prop.class, "getInstance3 lng="+lng);
		}
		if (lng==null)
		{
			//PageLng
			lng = (String)session.getAttribute("lng");
			//Logger.debug(Prop.class, "getInstance2 lng="+lng);
		}
		if (lng == null)
		{
			lng = Constants.getString("defaultLanguage");
			//Logger.debug(Prop.class, "getInstance4 lng="+lng);
		}
		if (request.getAttribute("PageLng")==null)
		{
			if (session.getAttribute(SESSION_I18N_PROP_LNG)==null) session.setAttribute(SESSION_I18N_PROP_LNG, lng);
		}
		return lng;
	}

	/**
	 * Vrati jazyk pre JS subory pre ckeditor, elfinder a podobne kde sa napr. pre CZ pouziva hodnota CS
	 * @param request
	 * @return
	 */
	public static String getLngForJavascript(HttpServletRequest request)
	{
		String lng = (String)request.getSession().getAttribute(Prop.SESSION_I18N_PROP_LNG);
		if (lng == null) lng = PageLng.getUserLng(request);

		if ("cz".equals(lng)) lng = "cs";
		else if (Tools.isEmpty(lng)) lng = "sk";

		return lng;
	}

	public static Prop getInstance(ServletContext servletContext, HttpSession session)
	{
		String lng = (String)session.getAttribute(SESSION_I18N_PROP_LNG);
		if (lng==null)
		{
			//PageLng
			lng = (String)session.getAttribute("lng");
		}
		if (lng == null)
		{
			lng = Constants.getString("defaultLanguage");
		}
		if (session.getAttribute(SESSION_I18N_PROP_LNG)==null) session.setAttribute(SESSION_I18N_PROP_LNG, lng);

		boolean refresh = false;

		return(getInstance(servletContext, lng, refresh));
	}

	public static Prop getInstance(String lng)
	{
		return(getInstance(Constants.getServletContext(), lng, false));
	}

	/**
	 * Obnovi instanciu Prop objektu ak je refresh nastavene na true
	 * @param refresh
	 * @return
	 */
	public static Prop getInstance(boolean refresh)
	{
		if (refresh)
		{
			Constants.getServletContext().removeAttribute(CONTEXT_I18N_PROP_HASHTABLE);
		}
		return(getInstance(Constants.getServletContext(), "sk", refresh));
	}

	public static Prop getInstance(ServletContext servletContext, String lng, boolean refresh)
	{
		if ("cs".equals(lng)) lng = "cz";

		//ziskaj properties subory
		Prop lngProp = getPropFromHashtable(lng, servletContext);

		//lazy load inych jazykov ako slovenskeho
		if (lngProp.getProperties().isEmpty() || refresh)
		{
			if (refresh)
			{
				lastUpdate = System.currentTimeMillis();
				lngProp.getProperties().clear();
			}
			lngProp.loadLanguageProp(lng, toFileNamePart(lng), DEFAULT_TEXTS, servletContext);
		}

		if (refresh)
		{
			Cache.getInstance().removeObjectStartsWithName("Prop.getDefaulProperties.");
			ClusterDB.addRefresh(Prop.class);
		}

		return(lngProp);
	}

	/**
	 *  Gets the text attribute of the Prop object
	 *
	 *@param  key     Description of the Parameter
	 *@return         The text value
	 */
	public String getText(String key)
	{
		if (properties == null)
		{
			return (key);
		}
		if (Tools.isEmpty(key)) return "";

		String value = properties.getProperty(key, key);

		//hladanie kluca podla prefixu (skupiny sablon, id sablony)
		List<String> prefixes = RequestBean.getTextKeyPrefixes();
		value = checkPrefixedValues(prefixes, key, value);

		//vyhladaj kluc podla domenoveho prefixu (podobne ako konf. premennu)
		value = checkDomainAlias(key, value);

		if (value.equals(key) && key.endsWith(".tooltip")==false && key.startsWith("displaytag.")==false && key.startsWith("[[#{")==false &&
			key.startsWith("components.translation_key.")==false && "&nbsp;".equals(key)==false && "ID".equals(key)==false)
		{
			//chybajuci string, pridaj ho do chybajucich
			missingTexts.putIfAbsent(language, Collections.synchronizedSet(new HashSet<MissingKeysDto>()));

			String urlAddress = "";
			RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
			if (rb != null) urlAddress = rb.getUrl();

			//check if key was allready set as missing, if yes -> just update lastMissing date value
			boolean present = false;
			for(MissingKeysDto missingKey : missingTexts.get(language)) {
				if(missingKey.getKey().equals(key)) {
					present = true;
					missingKey.setLastMissing(new Date());
					if (Tools.isNotEmpty(urlAddress)) missingKey.setUrlAddress(urlAddress);
					break;
				}
			}

			//if key is missing first time, add new MissingKeysDto variable into set of missing keys
			if(!present) {
				missingTexts.get(language).add(new MissingKeysDto(key, new Date(), language, urlAddress));
			}

			//try defaultLanguage property
            String defaultLanguage = Constants.getString("defaultLanguage");
            if (Tools.isNotEmpty(defaultLanguage) && "sk".equalsIgnoreCase(defaultLanguage)==false)
            {
                IwayProperties defaultProp = getRes(defaultLanguage);
                if (defaultProp != null)
                {
                    value = checkPrefixedValues(prefixes, key, defaultProp.getProperty(key, key));
                }
            }

            if (value.equals(key))
            {
                //try default properties
                IwayProperties skProp = getRes("sk");
                if (skProp != null) {
                    value = checkPrefixedValues(prefixes, key, skProp.getProperty(key, key));
                }
            }
		}
		if (value.startsWith("docid:") && value.length()<20)
		{
			try
			{
				int docId = Tools.getIntValue(value.substring(value.indexOf(":")+1), -1);
				if (docId > 0)
				{
					DocDB docDB = DocDB.getInstance();
					DocDetails doc = docDB.getDoc(docId);
					if (doc != null && Tools.isNotEmpty(doc.getData()))
					{
						value = doc.getData();
					}
				}
			}
			catch (Exception ex)
			{
				Logger.error(Prop.class, ex);
			}
		}

        RequestBean requestBean = SetCharacterEncodingFilter.getCurrentRequestBean();
		if (requestBean != null && requestBean.getParameter("showTextKeys") != null && requestBean.isUserAdmin()) {
			value = "[" + key + "] " + value;
		}

		return (value);
	}

    /**
     * Overi, ci existuje kluc s prefixom, vrati sa prvy co sa s prefixom najde (podla poradia v liste prefixov)
     * @param prefixes - zoznam kontrolovanych prefixov
     * @param key - textovy kluc
     * @param currentValue - aktualna hodnota textu (pred kontrolou na prefix, ak sa nenajde, vrati sa toto)
     * @return
     */
	private String checkPrefixedValues(List<String> prefixes, String key, String currentValue)
    {
        String value = currentValue;
        if (prefixes!=null)
        {
            String prefixedValue = null;
            for (String prefix : prefixes)
            {
                prefixedValue = properties.getProperty(prefix+"."+key, prefixedValue);
            }
            if (prefixedValue!=null) value = prefixedValue;
        }

        return value;
	 }


	private String checkDomainAlias(String key, String currentValue)
	{
		if (Constants.isConstantsAliasSearch()==false || key==null) return currentValue;

		RequestBean requestBean = SetCharacterEncodingFilter.getCurrentRequestBean();
		if(requestBean!=null && Tools.isNotEmpty(requestBean.getDomain()))
		{
			String domainAlias = MultiDomainFilter.getDomainAlias(requestBean.getDomain());
			if (Tools.isEmpty(domainAlias))
			{
				domainAlias = requestBean.getDomain();
			}
			if(Tools.isNotEmpty(domainAlias))
			{
				String keyWithAlias=domainAlias+"-"+key;
				//ak taky zaznam s aliasom v hashtabulke existuje, tak sa vrati jeho meno, inak vrati defaultne meno
				String valueWithAlias = properties.getProperty(keyWithAlias, null);
				if (valueWithAlias != null && valueWithAlias.equals(keyWithAlias)==false) return valueWithAlias;
			}
		}
		return currentValue;
	}

	public String getText(String key, String param1)
	{
		return(getTextWithParams(key, param1));
	}

	public String getText(String key, String param1, String param2)
	{
		return(getTextWithParams(key, param1, param2));
	}

	public String getText(String key, String param1, String param2, String param3)
	{
		return(getTextWithParams(key, param1, param2, param3));
	}

	public String getText(String key, String param1, String param2, String param3, String param4)
	{
		return(getTextWithParams(key, param1, param2, param3, param4));
	}

	public String getText(String key, String param1, String param2, String param3, String param4, String param5)
	{
		return(getTextWithParams(key, param1, param2, param3, param4, param5));
	}

	public String getText(String key, String param1, String param2, String param3, String param4, String param5, String param6)
	{
		return(getTextWithParams(key, param1, param2, param3, param4, param5, param6));
	}

	public String getText(String key, String param1, String param2, String param3, String param4, String param5, String param6, String param7)
	{
		return(getTextWithParams(key, param1, param2, param3, param4, param5, param6,param7));
	}

	public String getTextWithParams(String key, String... params)
	{
		String value = getText(key);
		int i = 1;
		for (String param : params)
		{
			value = Tools.replace(value, "!"+i, param);
			value = Tools.replace(value, "{"+i+"}", param);
			i++;
		}
		while (i <= 7)
		{
			//replacni placeholdre, je to do 7 pretoze tak fungovala povodna verzia
			value = Tools.replace(value, "!"+i, "");
			value = Tools.replace(value, "{"+i+"}", "");
			i++;
		}
		return value;
	}

	public Map<String,String> getTextStartingWith(String prefix)
	{
		Map<String,String> result = new HashMap<>();
		for (Map.Entry<String,String> entry : properties.entrySet())
		{
			if (entry.getKey().startsWith(prefix)) result.put(entry.getKey(), entry.getValue());
		}
		return result;
	}

	public static List<Map.Entry<String, String>> sortByValue(Map<String, String> texts)
	{
		List<Map.Entry<String, String>> list = setToList(texts.entrySet());

		/*Collections.sort(list, new Comparator<Map.Entry<String, String>>()
		{
			@Override
			public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2)
			{
				return o1.getValue().compareTo(o2.getValue());
			}
		});*/
		list.sort((Map.Entry<String, String> o1, Map.Entry<String, String> o2) -> o1.getValue().compareTo(o2.getValue()));

		return list;
	}

	public static List<Map.Entry<String, String>> sortByKey(Map<String, String> texts)
	{
		List<Map.Entry<String, String>> list = setToList(texts.entrySet());

		/*Collections.sort(list, new Comparator<Map.Entry<String, String>>()
		{
			@Override
			public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2)
			{
				return o1.getKey().compareTo(o2.getKey());
			}
		});*/
		list.sort((Map.Entry<String, String> o1, Map.Entry<String, String> o2) -> o1.getKey().compareTo(o2.getKey()));

		return list;
	}

	public String getTextWithSuffix(String key, String suffix){
		String suffixKey = key+"."+suffix;
		String text = suffixKey;

		String textWithSuffix = getText(suffixKey);
		String textWithoutSuffix = getText(key);

		if (Tools.isNotEmpty(textWithSuffix) && !suffixKey.equals(textWithSuffix)){
		    text = textWithSuffix;
        }else if(Tools.isNotEmpty(textWithoutSuffix) && !key.equals(textWithoutSuffix)){
		    text = textWithoutSuffix;
        }

		return text;
	}

	private static List<Map.Entry<String, String>> setToList(Set<Map.Entry<String, String>> set)
	{
		List<Map.Entry<String, String>> list = new ArrayList<>(set);

		return list;
	}

	/**
	 * Staticke ziskanie prekladoveho textu ked nemam request ani session, zvoli naposledy pouzity jazyk
	 * @param key
	 * @param params
	 * @return
	 */
	public static String getTxt(String key, String... params)
	{
		String lng = Constants.getString("defaultLanguage");
		RequestBean reqb = SetCharacterEncodingFilter.getCurrentRequestBean();
		if (reqb != null) lng = reqb.getLng();

		Prop prop = Prop.getInstance(lng);

		String value = prop.getText(key);

		if (params != null)
		{
			int counter = 1;
			for (String param : params)
			{
				value = Tools.replace(value, "!"+counter, param);
				value = Tools.replace(value, "{"+counter+"}", param);
			}
		}

		return value;
	}

	public String getTextProp(String key) {
		if (properties == null) {
			return null;
		}

		String value = properties.getProperty(key, null);

		return value;
	}

	public IwayProperties getProperties()
	{
		return properties;
	}

	public IwayProperties getRes(String lng)
	{
		Prop prop = getPropFromHashtable(lng);
		return prop.getProperties();
	}

	/**
	 * nacitanie len default textov pre jazyk
	 * @param languageKey
	 * @param fileLng
	 * @param servletContext
	 * @return
	 */
	public static IwayProperties getDefaulProperties(String languageKey, String fileLng, ServletContext servletContext)
	{
		//use cache, file should not change frequently
		Cache c = Cache.getInstance();
		String KEY = "Prop.getDefaulProperties."+languageKey+"."+fileLng;
		IwayProperties result = (IwayProperties) c.getObject(KEY);
		if (result!=null) return result;

		final IwayProperties defaultTexts = new IwayProperties("utf-8");
		if (Tools.isEmpty(languageKey)) languageKey = "sk";
		fileLng = (Tools.isEmpty(fileLng) || "sk".equals(fileLng)) ? "" : fileLng.startsWith("_") ? fileLng : "_"+fileLng;

		try
		{
			String fileName;
			fileName = "/text" + fileLng + ".properties";
			// toto loadujem aj z JARu
			IwcmFile f = new IwcmFile(Tools.getRealPath("/WEB-INF/classes"+fileName));
			if (f.exists())
			{
				Logger.println(Prop.class, " -> loading prop [" + languageKey + "]: " + fileName);
				defaultTexts.load(f);
			}
			String propInstallNames = Constants.getString("propInstallNames");
			if (Tools.isEmpty(propInstallNames))
				propInstallNames = Constants.getInstallName();

			//ak je nastavene, skus aj logInstallName
			if (Tools.isNotEmpty(Constants.getLogInstallName())) propInstallNames += ","+Constants.getLogInstallName();
			//aby sa nacitalo text-webjet9.properties
			propInstallNames = Constants.getString("defaultSkin")+","+propInstallNames;

			StringTokenizer st = new StringTokenizer(propInstallNames, ",");
			while (st.hasMoreTokens())
			{
				String installName = st.nextToken();
				// toto loadujem vzdy z classes adresara
				fileName = "/text" + fileLng + "-" + installName + ".properties";
				f = new IwcmFile(Tools.getRealPath("/WEB-INF/classes"+fileName));
				if (f.exists())
				{
					Logger.println(Prop.class, " -> loading prop [" + languageKey + "][" + installName + "]: " + fileName);
					defaultTexts.load(f);
				}
			}
		}
		catch (Exception e)
		{
			Logger.error(e);
		}

		c.setObjectSeconds(KEY, defaultTexts, 10*60, true);

		return defaultTexts;
	}

	/**
	 * Vrati texty zmenene pouzivatelom pre zadany jazyk.
	 * Vracia ich ako zoznam vo forme Kluc => hodnota.
	 *
	 * @param language
	 * @return
	 */
	public static IwayProperties getChangedProperties(String language){
		return getChangedProperties(language, null);
	}


	/**
	 * Vrati texty zmenene pouzivatelom pre zadany jazyk a prefix.
	 * Vracia ich ako zoznam vo forme Kluc => hodnota.
	 *
	 * @param language
	 * @param prefix
	 * @return
	 */
	public static IwayProperties getChangedProperties(String language, String prefix){
		String[] languages = Constants.getArray("languages");
		if (!ArrayUtils.contains(languages, language))
			throw new IllegalArgumentException("Unsupported language: ['"+language+"']");

		final IwayProperties changedTexts = new IwayProperties("utf-8");

		//nacitaj custom nazvy z databazy
		if(Tools.isEmpty(prefix))
		{
			new ComplexQuery().
				setSql("SELECT * FROM " +ConfDB.PROPERTIES_TABLE_NAME+" WHERE lng = ? ORDER BY update_date ASC").
				setParams(language).
				list(new Mapper<Void>(){
					@Override
					public Void map(ResultSet rs) throws SQLException
					{
						String key = DB.getDbString(rs, "prop_key");
						String value = DB.getDbString(rs, "prop_value");
						if (key != null) key = key.trim();
						changedTexts.setProperty(key, value);
						return null;
					}

				});
		}
		else
		{
			new ComplexQuery().
			setSql("SELECT * FROM " +ConfDB.PROPERTIES_TABLE_NAME+" WHERE lng = ? AND prop_key LIKE ? ORDER BY update_date ASC").
			setParams(language, prefix+"%").
			list(new Mapper<Void>(){
				@Override
				public Void map(ResultSet rs) throws SQLException
				{
					String key = DB.getDbString(rs, "prop_key");
					String value = DB.getDbString(rs, "prop_value");
					if (key != null) key = key.trim();
					changedTexts.setProperty(key, value);
					return null;
				}

			});
		}

		//vratime nase texty
		return changedTexts;
	}

	/**
	 * @return Returns the lastUpdate.
	 */
	public boolean shouldBeUpdated()
	{
		return lastUpdate > created;
	}


	/**
	 * Vrati true / false podla toho, ci ma nahrate properties objekty, alebo nie
	 * @return
	 */
	/*
	private boolean isEmpty()
	{
		if (properties == null) return false;

		return properties.isEmpty();
	}

	private void load(File f) throws IOException
	{
		if (properties == null) properties = new IwayProperties();
		properties.load(f);
	}

	private void setProperty(String key, String value)
	{
		if (properties == null) properties = new IwayProperties();
		properties.setProperty(key, value);
	}

	public String getProperty(String key)
	{
		return getProperty(key, key);
	}

	public String getProperty(String key, String defaultValue)
	{
		if (properties == null) return defaultValue;
		return properties.getProperty(key, defaultValue);
	}

	public Enumeration<String> propertyNames()
	{
		return properties.propertyNames();
	}
	*/

	/**
	 * returns set of missing texts for language param
	 * @param lng
	 * @return
	 */
	public static Set<MissingKeysDto> getMissingTexts(String lng) {
		if(missingTexts.containsKey(lng))
			return missingTexts.get(lng);

		return null;
	}

	/**
	 * returns set of missing texts for ALL language's
	 * @return
	 */
	public static Set<MissingKeysDto> getMissingTexts() {
		Set<MissingKeysDto> allMissingTexts = new HashSet<>();

		String[] lngArr = Constants.getArray("languages");
		for(String lng : lngArr)
			if(missingTexts.containsKey(lng))
				allMissingTexts.addAll(missingTexts.get(lng));

		return allMissingTexts;
	}

	/**
	 * Clears all missing texts
	 */
	public static void clearMissingTexts() {
		missingTexts.clear();
	}

	/**
	 * Delete missing text by key and language
	 * @param key
	 * @param lng
	 */
	public static void deleteMissingText(String key, String lng) {
		Set<MissingKeysDto> keysByLng = missingTexts.get(lng);
		for(MissingKeysDto dto : keysByLng) {
			if(dto.getKey().equals(key)) {
				keysByLng.remove(dto);
				break;
			}
		}
	}
}