-
Notifications
You must be signed in to change notification settings - Fork 53
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
base: master
Are you sure you want to change the base?
Tasks 1-4 #29
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Podoltseva Olga |
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; | ||
|
||
/** | ||
* | ||
|
@@ -16,35 +18,83 @@ public class BeanGraph { | |
* @param value - объект, привязанный к вершине | ||
*/ | ||
public BeanVertex addVertex(Bean value) { | ||
return null; | ||
if (value == null) { | ||
throw new NullPointerException("null value"); | ||
} | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если встретился цикл, то кидаем CycleReferenceException. Более логично было бы
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Вот за это спасибо. Все никак не могла придумать костыль. Просто я столько исключений бросаю при обработке xml-документа, что не подумала вставить исключение в граф. Я не спорю, может, в вашем графе так и было, но я узнала, что есть готовый код, который можно использовать, только под конец, когда писала инициализацию классов. Да и когда я увидела эпичный dfs в одну строчку (видимо, с лямбдой, да?), дальше смотреть мне перехотелось :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здорово, что удалось разобраться. При обработке xml можно перехватывать все внутренние исключения и наружу пробрасывать что-то обобщенное, это упростит код. В dfs действительно использовал стримы. Но вроде Рустам рассказывал о них и теперь вы сможете их прочитать. Он не в одну строчку конечно, там есть еще один метод-хэлпер. |
||
} | ||
if (v.getColor() == BeanVertex.Color.WHITE) { | ||
dfs(v, sortedList); | ||
} | ||
} | ||
vertex.setColor(BeanVertex.Color.BLACK); | ||
sortedList.add(vertex); | ||
return true; | ||
} | ||
|
||
} |
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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я узнаю свой код, он слишком доступен для Вас? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А вот тут обидненько. Да, под конец я использовала ваш красивенький готовый код, но тут-то видно отличия. Когда я последний раз смотрела, во-первых, type там был типа Type, а не Class<?>, как в документации у Field.getType(), а во-вторых, тут же задание со звездочкой. Вы вроде как вызывали field.set(), а я достаю из класса именованный метод. Правда я не нашла, как красиво можно у String сделать первую букву заглавной, поэтому выглядит эта строчка как что-то страшное. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
} | ||
|
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мы не бросаем NullPointerException руками, говорили об этом на лекции про исключения. Это ошибка. Вы может бросить IllegalArgumentException, если хотите валидировать входящие параметры
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Хорошо. Это я исправлю, мой косяк. Просто если бы лекции не пересекались с основной учебой и собраниями команды (опять же по основной учебе), я бы знала об этом. Это просто по привычке.
There was a problem hiding this comment.
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 в таком виде, как у вас. Иначе будет сложно отличить, руками это бросили или же действительно имеет место ошибка.