package com.bizosys.hsearch.index; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.bizosys.hsearch.filter.IStorable; import com.bizosys.hsearch.filter.Storable; import com.bizosys.hsearch.hbase.HReader; import com.bizosys.hsearch.hbase.HWriter; import com.bizosys.hsearch.hbase.NV; import com.bizosys.hsearch.schema.IOConstants; import com.bizosys.hsearch.util.RecordScalar; import com.bizosys.oneline.ApplicationFault; import com.bizosys.oneline.SystemFault; import com.bizosys.oneline.util.StringUtils; public abstract class TypeCode { protected Map<String, Map<String, Byte>> tenantTypeCodes = new ConcurrentHashMap<String, Map<String, Byte>>(); /** * For a given tenant and type code, it gives all the available type codes. * @param tenant * @param type * @return * @throws ApplicationFault * @throws SystemFault */ public Byte getTypeCode(String tenant, String type) throws ApplicationFault, SystemFault { if ( StringUtils.isEmpty(type)) return null; /** Tenant is cached and has the typecode */ Map<String, Byte> cachedCodes = null; if ( tenantTypeCodes.containsKey(tenant) ) { cachedCodes = tenantTypeCodes.get(tenant); if (cachedCodes.containsKey(type)) return cachedCodes.get(type); } /** In any case make a fresh loading */ cachedCodes = load(tenant); /** First time setting. Set it and return */ if ( null == cachedCodes ) { cachedCodes = getDefaultCodes(); if ( null == cachedCodes ) cachedCodes = new HashMap<String, Byte>(); } tenantTypeCodes.put(tenant, cachedCodes); /** Found in the fresh copy */ if (cachedCodes.containsKey(type)) { return cachedCodes.get(type); } /** Auto Insert and serve */ autoInsert(cachedCodes, type); persist(tenant, cachedCodes); return cachedCodes.get(type); } /** * Finds a blank space and insert te new type code there. * @param typeCodes * @param newType * @throws ApplicationFault */ public void autoInsert(Map<String, Byte> typeCodes, String newType) throws ApplicationFault { boolean[] filledPos = new boolean[256]; Arrays.fill(filledPos, false); for (int b : typeCodes.values()) { b = b - Byte.MIN_VALUE; filledPos[b] = true; } //Take the immediate open position for ( int i=0; i<filledPos.length; i++) { if ( filledPos[i]) continue; typeCodes.put(newType, (byte) (i - Byte.MIN_VALUE)); return; } throw new ApplicationFault ("No type code slots available"); } protected Map<String, Byte> load(String tenant, byte[] key) throws SystemFault, ApplicationFault { NV nv = new NV(IOConstants.NAME_VALUE_BYTES, IOConstants.NAME_VALUE_BYTES); RecordScalar scalar = new RecordScalar(key, nv); HReader.getScalar(IOConstants.TABLE_CONFIG, scalar); if ( null == nv.data) return null; //Virgin, Nothing is set yet. byte[] bytes = nv.data.toBytes(); int total = bytes.length; int pos = 0; byte len = 0; Map<String, Byte> newTypes = new HashMap<String, Byte>(256); while ( pos < total) { len = bytes[pos++]; if ( len < 1) continue; byte[] typeB = new byte[len]; if ( (pos + len) > bytes.length ) { IndexLog.l.warn("TypeCode Loading Issue :" + bytes.length + "/" + pos + "/" + len); break; } System.arraycopy(bytes, pos, typeB, 0,len); pos = pos + len; byte typeCode = bytes[pos++]; newTypes.put(new String(typeB), typeCode); } return newTypes; } public void persist(String tenant, Map<String, Byte> types, byte[] key) throws SystemFault { int totalSize = 0; try { for (String type : types.keySet()) { if ( StringUtils.isEmpty(type)) continue; totalSize = totalSize + 1 /** Type char length */ + type.length() + 1 /** Reserved for byte mapping */; } if ( 0 == totalSize ) return; byte[] bytes = new byte[totalSize]; int pos = 0, len = 0; for (String type : types.keySet()) { if ( StringUtils.isEmpty(type)) continue; len = type.length(); bytes[pos++] = (byte)len; System.arraycopy(type.getBytes(), 0, bytes, pos,len); pos = pos + len; bytes[pos++] = types.get(type); } NV nv = new NV(IOConstants.NAME_VALUE_BYTES, IOConstants.NAME_VALUE_BYTES, new Storable(bytes)); RecordScalar record = new RecordScalar(new Storable(key), nv); HWriter.getInstance(true).insertScalar(IOConstants.TABLE_CONFIG, record); tenantTypeCodes.put(tenant, types); } catch (IOException e) { IndexLog.l.fatal(toXml(types, "XXX")); throw new SystemFault(e); } } public abstract void persist(String tenant, Map<String, Byte> types) throws SystemFault; public abstract Map<String, Byte> load(String tenant) throws SystemFault, ApplicationFault; public abstract void truncate(String tenant) throws SystemFault, ApplicationFault; public abstract Map<String, Byte> getDefaultCodes() throws SystemFault, ApplicationFault; protected String toXml(Map<String, Byte> codes, String type) { if ( null == codes) return "<codes></codes>"; StringBuilder sb = new StringBuilder(); sb.append("<codes>"); if ( null != codes) { for (String termType: codes.keySet()) { sb.append('<').append(type).append('>'); sb.append("<type>").append(termType).append("</type>"); sb.append("<code>").append( (int) codes.get(termType)).append("</code>"); sb.append("</").append(type).append('>'); } } sb.append("</codes>"); return sb.toString(); } public String toString(Map<String, Byte> codes) { StringBuilder sb = new StringBuilder(); if ( null != codes) { for (String termType: codes.keySet()) { sb.append(termType).append('='); sb.append((int) codes.get(termType)).append('\n'); } } return sb.toString(); } protected void deleteCode(String strKey) throws SystemFault { IStorable key = new Storable(strKey); try { HWriter.getInstance(true).delete(IOConstants.TABLE_CONFIG, key); } catch (IOException e) { throw new SystemFault(e); } } }