Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tasks 1-4 #29

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions OWNER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Podoltseva Olga
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.5</version>
</dependency>
</dependencies>


Expand Down
2 changes: 1 addition & 1 deletion src/main/java/arhangel/dim/container/Bean.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
public class Bean {

private String name; // Уникально имя бина
private String name; // Уникальное имя бина
private String className; // Класс бина
private Map<String, Property> properties; // Набор полей бина ИмяПоля-Значение

Expand Down
60 changes: 55 additions & 5 deletions src/main/java/arhangel/dim/container/BeanGraph.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package arhangel.dim.container;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
*
Expand All @@ -16,35 +18,83 @@ public class BeanGraph {
* @param value - объект, привязанный к вершине
*/
public BeanVertex addVertex(Bean value) {
return null;
if (value == null) {
throw new NullPointerException("null value");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мы не бросаем NullPointerException руками, говорили об этом на лекции про исключения. Это ошибка. Вы может бросить IllegalArgumentException, если хотите валидировать входящие параметры

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хорошо. Это я исправлю, мой косяк. Просто если бы лекции не пересекались с основной учебой и собраниями команды (опять же по основной учебе), я бы знала об этом. Это просто по привычке.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мы говорили, что NPE - это в 99% случаев ошибка разработчика, аналог segfault в C. Если вылетает NPE - то значит мы где-то недоглядели. Поэтому избегаем использовать NPE в таком виде, как у вас. Иначе будет сложно отличить, руками это бросили или же действительно имеет место ошибка.

}
BeanVertex newVertex = new BeanVertex(value);
vertices.put(newVertex, new ArrayList<>());
return newVertex;
}

/**
* Соединить вершины ребром
* @param from из какой вершины
* @param to в какую вершину
*/
public void addEdge(BeanVertex from ,BeanVertex to) {
public void addEdge(BeanVertex from, BeanVertex to) {
if (from == null || to == null) {
throw new NullPointerException("null vertex");
}
if (!isConnected(from, to)) {
vertices.get(from).add(to);
}
}

/**
* Проверяем, связаны ли вершины
*/
public boolean isConnected(BeanVertex v1, BeanVertex v2) {
return false;
return getLinked(v1).contains(v2);
}

/**
* Получить список вершин, с которыми связана vertex
*/
public List<BeanVertex> getLinked(BeanVertex vertex) {
return null;
return vertices.get(vertex);
}

/**
* Количество вершин в графе
*/
public int size() {
return 0;
return vertices.size();
}

/**
* Топологическая сортировка
* Предполагаем, что все верщины до начала сортировки белые
* @return null, если в графе есть цикл, иначе массив отсортированных вершин
*/
public List<BeanVertex> topSort() {
List<BeanVertex> sortedList = new ArrayList<>();
Set<BeanVertex> vertexSet = vertices.keySet();
for (BeanVertex vertex : vertexSet) {
if (!dfs(vertex, sortedList)) {
return null;
}
}
return sortedList;
}

/**
* Обход дерева в глубину, с проверкой на цикличность
* @param vertex вершина, с которой начинаем обход
* @return true, если все успешно, false, если встретился цикл
*/
public boolean dfs(BeanVertex vertex, List<BeanVertex> sortedList) {
vertex.setColor(BeanVertex.Color.GRAY);
for (BeanVertex v : getLinked(vertex)) {
if (v.getColor() == BeanVertex.Color.GRAY) {
return false;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если встретился цикл, то кидаем CycleReferenceException. Более логично было бы

void dfs(...) throws CycleReferenceException {
  if (isCyclic) {
    throw new CycleReferenceException("Cycle near to " + v);
  }

}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот за это спасибо. Все никак не могла придумать костыль. Просто я столько исключений бросаю при обработке xml-документа, что не подумала вставить исключение в граф. Я не спорю, может, в вашем графе так и было, но я узнала, что есть готовый код, который можно использовать, только под конец, когда писала инициализацию классов. Да и когда я увидела эпичный dfs в одну строчку (видимо, с лямбдой, да?), дальше смотреть мне перехотелось :-)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здорово, что удалось разобраться. При обработке xml можно перехватывать все внутренние исключения и наружу пробрасывать что-то обобщенное, это упростит код.
О, dfs оказывается тоже утек в репозиторий... Я старался скрыть часть реализации, но видимо недостаточно внимательно комитил.

В dfs действительно использовал стримы. Но вроде Рустам рассказывал о них и теперь вы сможете их прочитать. Он не в одну строчку конечно, там есть еще один метод-хэлпер.

}
if (v.getColor() == BeanVertex.Color.WHITE) {
dfs(v, sortedList);
}
}
vertex.setColor(BeanVertex.Color.BLACK);
sortedList.add(vertex);
return true;
}

}
17 changes: 17 additions & 0 deletions src/main/java/arhangel/dim/container/BeanVertex.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@
* Вершина графа, которая содержит бин
*/
public class BeanVertex {

public enum Color {
WHITE,
GRAY,
BLACK
}

private Bean bean;
private Color color;

public BeanVertex(Bean bean) {
this.bean = bean;
this.color = Color.WHITE;
}

public Bean getBean() {
Expand All @@ -17,4 +26,12 @@ public Bean getBean() {
public void setBean(Bean bean) {
this.bean = bean;
}

public void setColor(Color color) {
this.color = color;
}

public Color getColor() {
return color;
}
}
65 changes: 59 additions & 6 deletions src/main/java/arhangel/dim/container/BeanXmlReader.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package arhangel.dim.container;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
*
*/
public class BeanXmlReader {
private static final String TAG_BEAN = "bean";
private static final String TAG_PROPERTY = "property";
Expand All @@ -14,8 +23,52 @@ public class BeanXmlReader {
private static final String ATTR_BEAN_ID = "id";
private static final String ATTR_BEAN_CLASS = "class";

public List<Bean> parseBeans(String pathToFile) {
return null;
public List<Bean> parseBeans(String pathToFile) throws IOException, JDOMException, InvalidConfigurationException {
SAXBuilder saxBuilder = new SAXBuilder();
File inputFile = new File(pathToFile);
if (!inputFile.exists()) {
throw new FileNotFoundException("File " + pathToFile + " doesn't exist");
}
Document document = saxBuilder.build(inputFile);
Element rootElement = document.getRootElement();
List<Element> beanList = rootElement.getChildren();
List<Bean> classList = new ArrayList<>();
for (Element bean : beanList) {
if (!bean.getName().equals(TAG_BEAN)) {
throw new InvalidConfigurationException("Incorrect tag: " + bean.getName());
}
if (bean.getAttributeValue(ATTR_BEAN_CLASS) == null || bean.getAttributeValue(ATTR_BEAN_ID) == null) {
throw new InvalidConfigurationException("Bean should have both class and id attributes");
}
if (classList.contains(bean.getAttributeValue(ATTR_BEAN_ID))) {
throw new InvalidConfigurationException("All beans should have unique ids");
}
List<Element> propertyList = bean.getChildren();
Map<String, Property> propertyMap = new HashMap<>();
for (Element prop : propertyList) {
if (!prop.getName().equals(TAG_PROPERTY)) {
throw new InvalidConfigurationException("Incorrect property: " + prop.getName());
}
if (prop.getAttributes().size() > 2) {
throw new InvalidConfigurationException("Property should have only 2 attributes: " +
ATTR_NAME + " and " + ATTR_REF + " or " + ATTR_VALUE);
}
String name = prop.getAttributeValue(ATTR_NAME);
if (name == null) {
throw new InvalidConfigurationException("Incorrect property name");
}
if (prop.getAttributeValue(ATTR_VALUE) != null) {
propertyMap.put(name, new Property(name, prop.getAttributeValue(ATTR_VALUE), ValueType.VAL));
} else if (prop.getAttributeValue(ATTR_REF) != null) {
propertyMap.put(name, new Property(name, prop.getAttributeValue(ATTR_REF), ValueType.REF));
} else {
throw new InvalidConfigurationException("Incorrect property attribute. " + name +
" should have " + ATTR_REF + " or " + ATTR_NAME);
}
}
classList.add(new Bean(bean.getAttributeValue(ATTR_BEAN_ID),
bean.getAttributeValue(ATTR_BEAN_CLASS), propertyMap));
}
return classList;
}

}
109 changes: 93 additions & 16 deletions src/main/java/arhangel/dim/container/Container.java
Original file line number Diff line number Diff line change
@@ -1,64 +1,141 @@
package arhangel.dim.container;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Используйте ваш xml reader чтобы прочитать конфиг и получить список бинов
*/
public class Container {
private List<Bean> beans;
private Map<String, Object> objectsByName = new HashMap<>();
private Map<String, Object> objectsByClass = new HashMap<>();

/**
* Если не получается считать конфиг, то бросьте исключение
* @throws InvalidConfigurationException неверный конфиг
*/
public Container(String pathToConfig) throws InvalidConfigurationException {

// вызываем BeanXmlReader
BeanXmlReader xmlReader = new BeanXmlReader();
try {
beans = xmlReader.parseBeans(pathToConfig);
} catch (Exception ex) {
throw new InvalidConfigurationException(ex.getMessage());
}

List<BeanVertex> sortVertices = beanSort();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CycleReferenceException вылетел бы здесь в случае цикла в конфигурации

if (sortVertices == null) {
throw new InvalidConfigurationException("Beans cycled");
}
try {
for (BeanVertex v : sortVertices) {
instantiateBean(v.getBean());
}
} catch (Exception ex) {
throw new InvalidConfigurationException(ex.getMessage());
}
}

/**
* Вернуть объект по имени бина из конфига
* Например, Car car = (Car) container.getByName("carBean")
*/
public Object getByName(String name) {
return null;
return objectsByName.get(name);
}

/**
* Вернуть объект по имени класса
* Например, Car car = (Car) container.getByClass("arhangel.dim.container.Car")
*/
public Object getByClass(String className) {
return null;
return objectsByClass.get(className);
}

private void instantiateBean(Bean bean) {

/*
// Примерный ход работы
private List<BeanVertex> beanSort() throws InvalidConfigurationException {
BeanGraph graph = new BeanGraph();
Map<String, BeanVertex> vertexByName = new HashMap<>();
for (Bean bean : beans) {
vertexByName.put(bean.getName(), graph.addVertex(bean));
}
for (Bean bean : beans) {
for (Map.Entry<String, Property> prop : bean.getProperties().entrySet()) {
if (prop.getValue().getType() == ValueType.REF) {
BeanVertex from = vertexByName.get(bean.getName());
BeanVertex to = vertexByName.get(prop.getValue().getValue());
if (to == null) {
throw new InvalidConfigurationException("WTF?");
}
graph.addEdge(from, to);
}
}
}
return graph.topSort();
}

private void instantiateBean(Bean bean) throws Exception {
String className = bean.getClassName();
Class clazz = Class.forName(className);
// ищем дефолтный конструктор
Object ob = clazz.newInstance();


for (String name : bean.getProperties().keySet()) {
// ищем поле с таким именен внутри класса
// учитывая приватные
Field field = clazz.getDeclaredField(name);
// проверяем, если такого поля нет, то кидаем InvalidConfigurationException с описание ошибки

if (field == null) {
throw new InvalidConfigurationException("Class " + className + " doesn't have field " + name);
}
// Делаем приватные поля доступными
field.setAccessible(true);
Property prop = bean.getProperties().get(name);
Class<?> type = field.getType();
Method setter = clazz.getDeclaredMethod("set" + Character.toUpperCase(name.charAt(0)) + name.substring(1),
type);
if (setter == null) {
throw new InvalidConfigurationException("Class " + className + " doesn't have setter for field " + name);
}
switch (prop.getType()) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я узнаю свой код, он слишком доступен для Вас?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А вот тут обидненько. Да, под конец я использовала ваш красивенький готовый код, но тут-то видно отличия. Когда я последний раз смотрела, во-первых, type там был типа Type, а не Class<?>, как в документации у Field.getType(), а во-вторых, тут же задание со звездочкой. Вы вроде как вызывали field.set(), а я достаю из класса именованный метод. Правда я не нашла, как красиво можно у String сделать первую букву заглавной, поэтому выглядит эта строчка как что-то страшное.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Извините меня, это моя ошибка, вы все сделали хорошо. Я не заметил, что этот кусок кода оказался открытым в нашем репозитории, хотел его спрятать. Вы абсолютно правы.

case VAL:
//field.set(ob, convert(type.getTypeName(), prop.getValue()));
setter.invoke(ob, convert(type.getTypeName(), prop.getValue()));
break;
case REF:
String refName = prop.getValue();
if (objectsByName.containsKey(refName)) {
//field.set(ob, objectsByName.get(refName));
setter.invoke(ob, objectsByName.get(refName));
} else {
throw new InvalidConfigurationException("Failed to instantiate bean. Field " + name);
}
break;
default:
}
objectsByName.put(bean.getName(), ob);
objectsByClass.put(bean.getClassName(), ob);
}

// Далее определяем тип поля и заполняем его
// Если поле - примитив, то все просто
// Если поле ссылка, то эта ссылка должа была быть инициализирована ранее

*/
}

// конвертирует строку в объект соответствующего типа
private Object convert(String typeName, String data) throws Exception {
switch (typeName) {
case "int":
case "Integer":
return Integer.valueOf(data);
case "double":
case "Double":
return Double.valueOf(data);
case "boolean":
case "Boolean":
return Boolean.valueOf(data);
default:
throw new InvalidConfigurationException("type name = " + typeName);
}
}


}
Loading