/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package framework.beans.address;
import framework.beans.directory.DirectoryBean;
import framework.beans.GenericEntity;
import framework.beans.address.entities.AddressDetails;
import framework.beans.address.entities.AddressObject;
import framework.beans.address.entities.AddressObjectDetails;
import framework.beans.address.entities.AddressObjectType;
import framework.generic.ClipsServerException;
import framework.security.UserRight;
import framework.security.UserRightsSetAbstract;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.ejb.Stateful;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* FIXME разобраться с добавлением удалением и переименованием
* @author petr
*/
@Stateful(mappedName="clips-beans/DirectoryKladrBean")
public class DirectoryKladrBean
extends DirectoryBean<AddressObject, AddressObjectDetails>
implements DirectoryKladrBeanRemote{
public final static long RUSSIA_CODE = 643;
public final static String OTHER_CODE_PREFIX = "-";
private static final String pattern1 = "^[НЧ]{1}\\([0-9]{1,}-[0-9]{1,}\\)$";
private static final String pattern3 = "^[0-9]{1,}-[0-9]{1,}$";
private int updateCount = 0;
private int persistCount = 0;
// private HashSet<Integer> toDelete;
public DirectoryKladrBean() {
super(AddressObject.class, "адресуемый объект");
}
@Override
protected UserRight getRightForCreateDirectoryItem() {
return UserRightsSetAbstract.CREATE_DIRECTORY_COMMON_ITEM;
}
@Override
protected UserRight getRightForWriteToDirectory() {
return UserRightsSetAbstract.WRITE_REGION_ADMIN_DIRECTORY;
}
/**
* Вернёт список корневых элементов
* @return список стран
*/
@Override
public List<AddressObjectDetails> getDirectory() {
Field[] f = new Field[1];
f[0] = new Field("parent", null, Field.OPERATOR_IS_NULL);
Iterator<AddressObject> it = findEntityList(AddressObject.class, f).iterator();
ArrayList<AddressObjectDetails> list = new ArrayList<AddressObjectDetails>();
while (it.hasNext()) {
AddressObject object = it.next();
list.add(object.getDetails(this));
}
return list;
}
/**
* FIXME
* @param entity
* @param details
* @throws ClipsServerException
*/
@Override
protected void set(AddressObject entity, AddressObjectDetails details) throws ClipsServerException {
AddressObjectDetails d = details;
AddressObject ao = entity;
ao.setActual(false);
ao.setIfnsCode(d.ifnsCode);
ao.setIfnsLocalCode(d.ifnsLocalCode);
ao.setOkato(d.okato);
ao.setParent(d.parentItem == 0 ? null : findAddressObject(d.parentItem));
if (d.parentItem != 0){//так обновляются дети
//Бубен, т.к. поле кода уникальное
Iterator it = findEntityWhat("a.kladrCode",
AddressObject.class,
new Field[]{new Field("actual", false),
new Field("parent", ao.getParent()),
new Field("kladrCode", "%.%", Field.OPERATOR_LIKE)},
"").iterator();
int c = 0;
boolean hasChildren = false;
while (it.hasNext()) {
hasChildren = true;
String s =(String)it.next();
int c1 = Integer.parseInt(s.substring(s.lastIndexOf(".")+1));
if (c1 > c){
c = c1;
}
}
if (hasChildren){
ao.setKladrCode(ao.getParent().getKladrCode() + "." + (c + 1));
}else{
ao.setKladrCode(ao.getParent().getKladrCode() + ".1");
}
}else{//так обновляются родители (страны)
String code = ao.getKladrCode();
ao.setKladrCode(d.kladrCode);
List<AddressObject> addressObjects
= findEntityList(AddressObject.class,
new Field[]{new Field("parent", null, Field.OPERATOR_NOT_NULL),
new Field("actual", false),
new Field("kladrCode", code + ".%", Field.OPERATOR_LIKE)});
if (ao.getId() != AddressObject.ADDRESS_OBJECT_RUSSIA){
for (AddressObject addressObject : addressObjects) {
String kladrCode = addressObject.getKladrCode();
int point = kladrCode.indexOf(".");
kladrCode = ao.getKladrCode() + kladrCode.substring(point);
addressObject.setKladrCode(kladrCode);
}
}
}
ao.setPostIndex(d.postIndex);
ao.setTitle(d.title);
//ao.setDirty(details.dirty);
ao.setType(findAddressObjectType(d.typeId));
}
@Override
public List<AddressObjectDetails> getItems(int parentId) throws ClipsServerException {
AddressObject parent = findAddressObject(parentId);
ArrayList<AddressObjectDetails> list = new ArrayList<AddressObjectDetails>();
List<AddressObject> it = findEntityList(AddressObject.class, "parent", parent, "ORDER by a.id");
// System.out.println("KLADR BEAN getItems for " + parent.getTitle());
for (AddressObject addressObject : it) {
System.out.println(" " + addressObject.getTitle());
list.add(addressObject.getDetails(this));
}
return list;
}
@Override
public List<AddressObjectDetails> getPath(int itemId) throws ClipsServerException {
AddressObject object = findAddressObject(itemId);
ArrayList<AddressObjectDetails> list = new ArrayList<AddressObjectDetails>();
while(object != null){
list.add(0, object.getDetails(this));
object = object.getParent();
}
return list;
}
/**
* синхронизует кладр
* @param roots
* @throws framework.generic.ClipsServerException
*/
@Override
public void syncDirectory(List<KladrItem> roots, Map<KladrCode, KladrCode> altnames) throws ClipsServerException {
// toDelete = new HashSet<Integer>();
Field[] f = new Field[]{new Field("kladrCode", altnames.values(), Field.OPERATOR_IN)};
Iterator iterator = findEntityList(AddressObject.class, f).iterator();
while (iterator.hasNext()) {
AddressObject ao = (AddressObject)iterator.next();
ao.setKladrCode(altnames.get(new KladrCode(ao.getKladrCode())).toString());
ao.setActual(false);
manager.merge(ao);
}
manager.flush();
for (KladrItem root : roots) {
syncRoot(root);
}
}
private AddressObject findAddressObject(int id) throws ClipsServerException {
return findEntity(AddressObject.class, id);
}
private AddressObjectType findAddressObjectType(int id) throws ClipsServerException {
return findEntity(AddressObjectType.class, id);
}
/**
* Синхронизует дерево начиная от корня
* @param roots
* @throws generic.ClipsServerException
*/
private void syncRoot(KladrItem root) throws ClipsServerException {
System.out.println("DIRECTORYCLADRBEAN update region: " + root);
AddressItem region = new AddressItem(root);
AddressObject russia = findAddressObject(AddressObject.ADDRESS_OBJECT_RUSSIA);
Field[] f = new Field[]{new Field("kladrCode", root.kladrCode.toString().substring(0, 2).toString()+"%", Field.OPERATOR_LIKE)/*,
new Field("kladrCode", nextCode, Field.OPERATOR_LESS)*/};
Iterator iterator = findEntityList(AddressObject.class, f).iterator();
System.out.println("DIRECTORYCLADRBEAN load from db");
int num = 0;
while (iterator.hasNext()) {
num++;
if (num % 1000 == 0){
System.out.println("DIRECTORYCLADRBEAN process records: " + num);
}
AddressObject addressObject = (AddressObject) iterator.next();
KladrCode code = new KladrCode(addressObject.getKladrCode());
AddressItem item = region.getChlidByCode(code);
if (item == null){
if (code.equals(root.kladrCode)){
region.entity = addressObject;
}else{
//remove this entity
// toDelete.add(addressObject.getId());
addressObject.setActual(false);
}
}else{
item.entity = addressObject;
}
}
System.out.println("DIRECTORYCLADRBEAN loded records: " + num);
System.out.println("DIRECTORYCLADRBEAN update");
updateCount = 0;
if (region.entity == null) {
region.entity = new AddressObject();
}
region.changed = fillEntity(region, russia);
if (region.changed){
if (region.entity.getId() == 0){
manager.persist(region.entity);
}else{
region.entity = manager.merge(region.entity);
}
}
updateRecoursive(region);
System.out.println("DIRECTORYCLADRBEAN process update: " + updateCount);
// throw new RuntimeException("Всё нормально, просто не обновляем");
manager.flush();
manager.flush();
manager.flush();
// if(!toDelete.isEmpty()){
// System.out.println("DIRECTORYCLADRBEAN delete: " + toDelete);
// Field[] del = new Field[]{new Field("id", toDelete, Field.OPERATOR_IN)};
// deleteEntityList(AddressObject.class.getSimpleName(), del);
// System.out.println("DIRECTORYCLADRBEAN process delete: " + toDelete.size());
// }
}
/**
* рекурсивно апдейтит детей
* @param item
* @throws generic.ClipsServerException
*/
private void updateRecoursive(AddressItem item) throws ClipsServerException{
for(AddressItem child : item.children){
if (child.entity == null) {
child.entity = new AddressObject();
}
child.changed = fillEntity(child, item.entity);
if (child.changed){
AddressObject addressObject = child.entity;
if (addressObject.getId() == 0){
manager.persist(addressObject);
}else{
child.entity = manager.merge(addressObject);
}
}
}
for(AddressItem child : item.children){
if (child.children != null){
updateRecoursive(child);
}
}
}
/**
* Проверка на наличие циклических ссылок
* @param object
* @throws generic.ClipsServerException
*/
public void checkCicrcle(AddressObject object) throws ClipsServerException{
AddressObject current = object.getParent();
while (current != null) {
if (current.getKladrCode().equals(object.getKladrCode())){
//
ArrayList<String> items = new ArrayList<String>(0){
@Override
public String toString() {
String s = new String();
for (Object object1 : this) {
s+=object1;
}
return s;
}
};
items.add(object.toString());
AddressObject current1 = object.getParent();
while (current1 != null) {
if (current.getKladrCode().equals(object.getKladrCode())){
items.add(current1.toString());
}
current1 = current1.getParent();
}
throw new ClipsServerException("Циклическая ссылка: " + items + ", замыкается на элементе: " + current);
}
current = current.getParent();
}
}
/**
* Заполняет сущность переданными данными
*
* @param entity сущность
* @param item итем с данными
* @param title сущность названия
* @return was entity updated
* @throws generic.ClipsServerException
*/
private boolean fillEntity(AddressItem item,
AddressObject parent) throws ClipsServerException{
AddressObjectType type = findAddressObjectType(item.item.typeId);
// if ((item.entity.getParent() != null)
// && (item.entity.getParent().getKladrCode().equals(item.entity.getKladrCode()))){
// System.out.println("FUCKING 498: "+ item);
// }
if (type == null){
throw new ClipsServerException("недопустимый тип адресуемого объекта: "
+ item.item.title + " type id: " + item.item.typeId
+ " обновите справочник типов");
}
if (item == null || item.entity == null || item.item == null){
throw new ClipsServerException("FUCKING NULL");
}
if (item.entity.getIfnsCode() != item.item.ifnsCode
|| item.entity.getIfnsLocalCode() != item.item.ifnsLocalCode
|| item.entity.getOkato() != item.item.okato
|| item.entity.getPostIndex() != item.item.postIndex
|| item.entity.getTitle() == null
|| !item.entity.getTitle().equals(item.item.title)
|| item.entity.getType() != type
|| item.entity.getKladrCode() == null
|| !item.entity.getKladrCode().equals(item.item.kladrCode.toString())
|| item.entity.getParent() != parent
|| !item.entity.isActual()){
updateCount++;
item.entity.setIfnsCode(item.item.ifnsCode);
item.entity.setIfnsLocalCode(item.item.ifnsLocalCode);
item.entity.setOkato(item.item.okato);
item.entity.setPostIndex(item.item.postIndex);
item.entity.setTitle(item.item.title);
item.entity.setType(type);
item.entity.setKladrCode(item.item.kladrCode.toString());
item.entity.setParent(parent);
item.entity.setActual(true);
checkCicrcle(item.entity);
if (updateCount % 1000 == 0){
System.out.println("DIRECTORYCLADRBEAN process update: " + updateCount);
}
return true;
}else{
return false;
}
}
@Override
public int getItemIDByCode(String code) throws ClipsServerException{
while (code.length() < 19) {
code = code + "0";
}
List<AddressObject> list = findEntityList(AddressObject.class, "kladrCode", code);
if (list.size() > 0) {
return list.get(0).getId();
} else {
return 0;
}
}
@Override
protected int saveEntity(GenericEntity entity) {
return super.saveEntity(entity);
}
/**
* Правит код объекта ориентируясь по номеру дома, но если передана не улица, то занулит
* @param details
* @return обновленные детали
* @throws ClipsServerException
*/
@Override
public AddressDetails correctDetails(AddressDetails details) throws ClipsServerException {
AddressObject street = findEntity(AddressObject.class, details.objectId);
//really street?
if (street != null && street.getType().getLevel() > AddressObjectType.ADDRESS_OBJECT_LEVEL_STREET){//block
//do nothing
}else if (street != null && street.getType().getLevel() == AddressObjectType.ADDRESS_OBJECT_LEVEL_STREET){//street
List<AddressObject> blocks = findEntityList(AddressObject.class, "parent", street, "ORDER by a.id");
for (AddressObject block : blocks) {
if (include(block, details.building)){
details.objectId = block.getId();
break;
}
}
}else{//not block not street - затереть данные о квартире и доме
details.building = "";
details.flat = "";
}
if (details.building.equals("0")){
details.building = "";
}
if (details.flat.equals("0")){
details.flat = "";
}
return details;
}
/**
* Проверяет входит ли дом с данным номером в квартал
* @param block
* @param buildingNumber
* @return
*/
private boolean include(AddressObject block, String buildingNumberString){
if (block.getType().getLevel() <= AddressObjectType.ADDRESS_OBJECT_LEVEL_STREET){
throw new IllegalArgumentException("Переданный адресуемый объект не является кварталом: " + block);
}else{
int buildingNumber = 0;
Pattern compiled = Pattern.compile("(^[0-9]+).*");
Matcher matcher = compiled.matcher(buildingNumberString);
boolean found = matcher.find();
if (found) {
String s = matcher.replaceAll("$1");
buildingNumber = Integer.parseInt(s);
}
ArrayList<String> numbers = createNumbers(block);
String s;
if (!buildingNumberString.equalsIgnoreCase("" + buildingNumber)){
for (int i = 0; i < numbers.size(); i++) {
s = numbers.get(i);
if (!Pattern.matches(pattern1, s) && !Pattern.matches(pattern3, s)) {//s не диапазон
if (s.equalsIgnoreCase(buildingNumberString)){
return true;
}
}
}
}
if (buildingNumber > 0){
for (int i = 0; i < numbers.size(); i++) {
s = numbers.get(i);
if (Pattern.matches(pattern1, s)) {//указана сторона
int breakPos = s.indexOf("-");
int start = Integer.parseInt(s.substring(2, breakPos));
int end = Integer.parseInt(s.substring(breakPos + 1, s.length() - 1));
if (buildingNumber % 2 == start % 2 && start <= buildingNumber && buildingNumber <= end){
return true;
}
} else if (Pattern.matches(pattern3, s)) {//без указания стороны
int breakPos = s.indexOf("-");
int start = Integer.parseInt(s.substring(0, breakPos));
int end = Integer.parseInt(s.substring(breakPos + 1, s.length()));
if (start <= buildingNumber && buildingNumber <= end){
return true;
}
}
}
}
for (int i = 0; i < numbers.size(); i++) {
s = numbers.get(i);
matcher = compiled.matcher(s);
found = matcher.find();
if (found) {
s = matcher.replaceAll("$1");
if (Integer.parseInt(s) == buildingNumber){
return true;
}
}
}
return false;
}
}
private ArrayList<String> createNumbers(AddressObject block) {
if (block.getType().getLevel() <= AddressObjectType.ADDRESS_OBJECT_LEVEL_STREET){
throw new IllegalArgumentException("Переданный адресуемый объект не является кварталом");
}else{
String title = block.getTitle().trim();
ArrayList<String> res = new ArrayList<String>(0);
String s = "";
for (int i = 0; i < title.length(); i++) {
if (title.charAt(i) == ',') {
res.add(s.trim());
s = "";
} else {
s += title.charAt(i);
}
}
res.add(s.trim());
return res;
}
}
/**
* Принимает строку в формате <субъект РФ><разделитель><район><разделитель><город><разделитель><сельслвет><разделитель><населённый пункт>
* <разделитель><улица><разделитель><№ дома><разделитель><№ кормуса/строения>
* @param address
* @return
*/
@Override
public int[] findAddressObject(String address, String br){
String[] split = address.split(br);
int lastLevel = 0;
ArrayList<String> compactTitles = new ArrayList<String>();
for (int i = split.length - 1; i >= 0; i--) {
String string = split[i].trim();
if (!string.isEmpty() && !string.startsWith("ДОМ") && !string.startsWith("кв.")){
compactTitles.add(string.trim());
if (lastLevel == 0){
lastLevel = i + 1;
}
}
}
ArrayList<Field> fields = new ArrayList<Field>();
String parent = "";
for (String string : compactTitles) {
fields.add(new Field(parent +"title", string));
parent += "parent.";
}
// fields.remove(0);
// fields.add(new Field("title", compactTitles.get(compactTitles.size()-1)));
fields.add(new Field("type.level", lastLevel));
List<AddressObject> list = findEntityList(AddressObject.class, fields);
int[] result = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
result[i] = list.get(i).getId();
}
return result;
}
}