diff --git a/ocksumoron/src/test/java/ru/fizteh/fivt/students/ocksumoron/helloworld/AppTest.java b/ocksumoron/src/test/java/ru/fizteh/fivt/students/ocksumoron/helloworld/AppTest.java
deleted file mode 100644
index ce7c89b2..00000000
--- a/ocksumoron/src/test/java/ru/fizteh/fivt/students/ocksumoron/helloworld/AppTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package test.java.ru.fizteh.fivt.students.helloworld;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Unit test for simple App.
- */
-public class AppTest
- extends TestCase
-{
- /**
- * Create the test case
- *
- * @param testName name of the test case
- */
-// public AppTest( String testName )
-// {
-// super( testName );
-// }
-
- /**
- * @return the suite of tests being tested
- */
- public static Test suite()
- {
- return new TestSuite( AppTest.class );
- }
-
- /**
- * Rigourous Test :-)
- */
- public void testApp()
- {
- assertTrue( true );
- }
-}
diff --git a/preidman/pom.xml b/preidman/pom.xml
index 7cd55159..b9b36869 100644
--- a/preidman/pom.xml
+++ b/preidman/pom.xml
@@ -16,11 +16,7 @@
-
- junit
- junit
- 3.8.1
-
+
org.twitter4j
twitter4j-stream
@@ -31,5 +27,66 @@
jcommander
1.48
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ + com.google.maps
+ + google-maps-services
+ + 0.1.9
+ +
+ +
+ + org.json
+ + json
+ + 20151123
+ +
+
+
+ org.powermock
+ powermock-module-junit4
+ 1.6.2
+ test
+
+
+
+ org.powermock
+ powermock-api-mockito
+ 1.6.2
+ test
+
+
+
+ commons-io
+ commons-io
+ 2.4
+ test
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.4
+
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.3
+ test
+
+
+
+
+ com.tngtech.java
+ junit-dataprovider
+ 1.10.1
+ test
+
+
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Aggregates.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Aggregates.java
new file mode 100644
index 00000000..eb803c19
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Aggregates.java
@@ -0,0 +1,102 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+
+public class Aggregates {
+
+ public interface Aggregate extends Function {
+ T forGroup(Set set);
+ }
+
+ private static Collection getValues(Collection set, Function expression) {
+ Set values = new HashSet<>();
+ for (C element : set) {
+ values.add(expression.apply(element));
+ }
+ return values;
+ }
+
+ public static > Aggregate max(Function expression) {
+ return new Aggregate() {
+
+ @Override
+ public T apply(C element) {
+ return (T) expression.apply(element);
+ }
+
+ @Override
+ public T forGroup(Set set) throws ClassCastException, NoSuchElementException {
+ return Collections.max(getValues(set, expression));
+ }
+ };
+ }
+
+ public static > Aggregate min(Function expression) {
+ return new Aggregate() {
+
+ @Override
+ public T apply(C element) {
+ return (T) expression.apply(element);
+ }
+
+ @Override
+ public T forGroup(Set set) throws ClassCastException, NoSuchElementException {
+ return Collections.min(getValues(set, expression));
+ }
+ };
+ }
+
+ public static > Aggregate count(Function expression) {
+ return new Aggregate() {
+
+ @Override
+ public Long apply(C element) {
+ return Long.valueOf(1);
+ }
+
+ @Override
+ public Long forGroup(Set set) {
+ return (long) set.size();
+ }
+ };
+ }
+
+ public static > Aggregate avg(Function expression) {
+ return new Aggregate() {
+
+ @Override
+ public T apply(C element) {
+ return expression.apply(element);
+ }
+
+ @Override
+ public T forGroup(Set set) throws ClassCastException, NoSuchElementException {
+ if (set.isEmpty()) {
+ throw new NoSuchElementException("empt");
+ }
+ T sample = expression.apply(set.iterator().next());
+ if (sample instanceof Long || sample instanceof Integer || sample instanceof Short) {
+ long average = 0;
+ for (C element : set) {
+ average += (Long) (expression.apply(element));
+ }
+ return (T) Long.valueOf(average / set.size());
+ } else if (sample instanceof Float || sample instanceof Double) {
+ double average = 0;
+ for (C element : set) {
+ average += (Double) (expression.apply(element));
+ }
+ return (T) Double.valueOf(average / set.size());
+ }
+ throw new ClassCastException("wrong type");
+ }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Conditions.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Conditions.java
new file mode 100644
index 00000000..11148b58
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Conditions.java
@@ -0,0 +1,22 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx;
+
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class Conditions {
+
+ public static Predicate rlike(Function expression, String regexp) {
+ return new Predicate() {
+
+ @Override
+ public boolean test(T element) {
+ return expression.apply(element).matches(regexp);
+ }
+ };
+ }
+
+ public static Predicate like(Function expression, String pattern) {
+ throw new UnsupportedOperationException();
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/OrderByConditions.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/OrderByConditions.java
new file mode 100644
index 00000000..2f83f653
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/OrderByConditions.java
@@ -0,0 +1,17 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx;
+
+import java.util.Comparator;
+import java.util.function.Function;
+
+public class OrderByConditions {
+
+
+ public static > Comparator asc(Function expression) {
+ return (o1, o2) -> expression.apply(o1).compareTo(expression.apply(o2));
+ }
+
+ public static > Comparator desc(Function expression) {
+ return (o1, o2) -> expression.apply(o2).compareTo(expression.apply(o1));
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Sources.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Sources.java
new file mode 100644
index 00000000..11a05ef5
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/Sources.java
@@ -0,0 +1,52 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+public class Sources {
+
+ @SafeVarargs
+ public static List list(T... items) {
+ return Arrays.asList(items);
+ }
+
+ @SafeVarargs
+ public static Set set(T... items) {
+ Set result = new HashSet<>();
+ for (T element : items) {
+ result.add(element);
+ }
+ return result;
+ }
+
+
+ public static Stream lines(InputStream inputStream) throws IOException, ClassCastException {
+ StringBuilder builder = new StringBuilder();
+ byte[] data = new byte[100];
+ int readed = inputStream.read(data);
+ while (readed > 0) {
+ builder.append(data);
+ readed = inputStream.read(data);
+ }
+ inputStream.close();
+ String[] lines = builder.toString().split("[\n]");
+ Stream.Builder stream = Stream.builder();
+ for (String line : lines) {
+ stream.add((T) line);
+ }
+ return stream.build();
+ }
+
+ public static Stream lines(Path file) throws ClassCastException, IOException {
+ InputStream input = new FileInputStream(file.toFile());
+ return lines(input);
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQueryExecuteException.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQueryExecuteException.java
new file mode 100644
index 00000000..6192f447
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQueryExecuteException.java
@@ -0,0 +1,8 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+public class CollectionQueryExecuteException extends Exception {
+
+ CollectionQueryExecuteException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQuerySyntaxException.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQuerySyntaxException.java
new file mode 100644
index 00000000..4578d389
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/CollectionQuerySyntaxException.java
@@ -0,0 +1,12 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+public class CollectionQuerySyntaxException extends Exception {
+
+ CollectionQuerySyntaxException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ CollectionQuerySyntaxException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FinalRow.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FinalRow.java
new file mode 100644
index 00000000..8ff4631c
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FinalRow.java
@@ -0,0 +1,31 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class FinalRow {
+ private R row;
+ private Set from;
+
+ FinalRow(R newRow, T elementFrom) {
+ from = new HashSet();
+ from.add(elementFrom);
+ row = newRow;
+ }
+
+ public R get() {
+ return row;
+ }
+
+ public Set getFrom() {
+ return from;
+ }
+
+ public T getAnyFrom() {
+ return from.iterator().next();
+ }
+
+ public void updateRow(R newRow) {
+ row = newRow;
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FromStmt.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FromStmt.java
new file mode 100644
index 00000000..8c094e08
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/FromStmt.java
@@ -0,0 +1,157 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+
+public final class FromStmt {
+ private Iterable base;
+ private ExecutedTable> previousTable = null;
+
+ private FromStmt() { }
+
+ private static final class ExecutedTable {
+ private final Iterable outputTable;
+ private final Class outputClass;
+
+ private ExecutedTable(Class clazz, Iterable table) {
+ outputTable = table;
+ outputClass = clazz;
+ }
+ }
+
+ public static FromStmt from(Iterable iterable) {
+ FromStmt stmt = new FromStmt<>();
+ stmt.base = iterable;
+ return stmt;
+ }
+
+ public static FromStmt from(Stream stream) {
+ FromStmt stmt = new FromStmt<>();
+ List list = new ArrayList<>();
+ stream.forEach(s -> list.add(s));
+ stmt.base = list;
+ return stmt;
+ }
+
+ public static FromStmt from(WhereStmt, T> subStmt) throws CollectionQueryExecuteException {
+ Iterable subQueryResult = subStmt.execute();
+ return from(subQueryResult);
+ }
+
+ void setPreviousPart(Class clazz, Iterable table) {
+ previousTable = new ExecutedTable(clazz, table);
+ }
+
+ private SelectStmt innerSelect(Class clazz, Function[] s, boolean isDistinct)
+ throws CollectionQuerySyntaxException {
+ if (previousTable == null) {
+ return new SelectStmt(base, null, clazz, isDistinct, s);
+ } else {
+ if (!previousTable.outputClass.equals(clazz)) {
+ throw new CollectionQuerySyntaxException("parts of union has different types");
+ }
+ return new SelectStmt(base, (Iterable) previousTable.outputTable, clazz, isDistinct, s);
+ }
+ }
+
+ @SafeVarargs
+ public final SelectStmt select(Class clazz, Function... s)
+ throws CollectionQuerySyntaxException {
+ return innerSelect(clazz, s, false);
+ }
+
+ @SafeVarargs
+ public final SelectStmt selectDistinct(Class clazz, Function... s)
+ throws CollectionQuerySyntaxException {
+ return innerSelect(clazz, s, true);
+ }
+
+ public static final class Tuple {
+ private F firstPart;
+ private S secondPart;
+
+ private Tuple(F first, S second) {
+ firstPart = first;
+ secondPart = second;
+ }
+
+ public F first() {
+ return firstPart;
+ }
+
+ public S second() {
+ return secondPart;
+ }
+ }
+
+
+ public final class JoinStmt {
+ private Iterable secondTable;
+
+ private JoinStmt(Iterable tableOnJoin) {
+ secondTable = tableOnJoin;
+ }
+
+ public FromStmt> on(Predicate> joiningPredicate) {
+ Collection> joinedCollection = new ArrayList<>();
+ for (T first : base) {
+ for (S second : secondTable) {
+ Tuple joinedRow = new Tuple(first, second);
+ if (joiningPredicate.test(joinedRow)) {
+ joinedCollection.add(joinedRow);
+ }
+ }
+ }
+ FromStmt> stmt = new FromStmt<>();
+ stmt.base = joinedCollection;
+ stmt.previousTable = previousTable;
+ return stmt;
+ }
+
+ public FromStmt> on(Function leftKey, Function rightKey) {
+ Map, Set>> possibleValues = new HashMap<>();
+ for (T row : base) {
+ R value = leftKey.apply(row);
+ if (value != null) {
+ if (!possibleValues.containsKey(value)) {
+ possibleValues.put(value, new Tuple<>(new HashSet<>(), new HashSet<>()));
+ }
+ possibleValues.get(value).firstPart.add(row);
+ }
+ }
+ for (S row : secondTable) {
+ R value = rightKey.apply(row);
+ if (value != null) {
+ if (!possibleValues.containsKey(value)) {
+ continue;
+ }
+ possibleValues.get(value).secondPart.add(row);
+ }
+ }
+
+ Collection> joinedCollection = new ArrayList<>();
+
+ possibleValues.forEach((v, tuple) -> {
+ for (T firstRow : tuple.first()) {
+ for (S secondRow : tuple.second()) {
+ joinedCollection.add(new Tuple(firstRow, secondRow));
+ }
+ }
+ });
+ return from(joinedCollection);
+ }
+ }
+
+ public JoinStmt join(Iterable secondTable) {
+ return new JoinStmt<>(secondTable);
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/SelectStmt.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/SelectStmt.java
new file mode 100644
index 00000000..6b6b79f9
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/SelectStmt.java
@@ -0,0 +1,254 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import java.util.Comparator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+
+import ru.fizteh.fivt.students.preidman.CollectionsQLEx.Aggregates.Aggregate;
+
+public class SelectStmt {
+
+ private Stream stream;
+ private Iterable baseCollection;
+ private Class outputClass;
+ private Iterable previousPart;
+ private Function[] convertFunctions;
+
+ private Function>[] groupingFunctions;
+ private Predicate groupingCondition = null;
+ private Comparator finalComparator = null;
+ private int finalLimit = -1;
+
+ private boolean isDistinct;
+
+ @SafeVarargs
+ SelectStmt(Iterable newBaseCollection, Iterable previousTable,
+ Class clazz, boolean distinct, Function... s) {
+ baseCollection = newBaseCollection;
+ previousPart = previousTable;
+ stream = StreamSupport.stream(newBaseCollection.spliterator(), false);
+ outputClass = clazz;
+ convertFunctions = s;
+ isDistinct = distinct;
+ groupingFunctions = null;
+ }
+
+ final Class getOutputClass() {
+ return outputClass;
+ }
+
+ private static Comparator getCombinedComparator(Iterable> comparators) {
+ return new Comparator() {
+ @Override
+ public int compare(T first, T second) {
+ for (Comparator comparator : comparators) {
+ int result = comparator.compare(first, second);
+ if (result != 0) {
+ return result;
+ }
+ }
+ return 0;
+ }
+ };
+ }
+
+ public final WhereStmt where(Predicate predicate) {
+ stream = stream.filter(predicate);
+ return new WhereStmt<>(this);
+ }
+
+ @SafeVarargs
+ public final void groupBy(Function>... expressions) throws CollectionQuerySyntaxException {
+ if (groupingFunctions != null) {
+ throw new CollectionQuerySyntaxException("group table in the query");
+ }
+ groupingFunctions = expressions;
+ }
+
+ public final void having(Predicate condition) throws CollectionQuerySyntaxException {
+ if (groupingCondition != null) {
+ throw new CollectionQuerySyntaxException("set grouping conditions");
+ }
+ groupingCondition = condition;
+ }
+
+ private class Applier implements Consumer {
+ private Collection> output;
+ private Constructor constructor;
+
+ Applier(Collection> outputCollection, Constructor resultConstructor) {
+ output = outputCollection;
+ constructor = resultConstructor;
+ }
+
+ @Override
+ public void accept(T element) {
+ Object[] parametrs = new Object[convertFunctions.length];
+ for (int i = 0; i < convertFunctions.length; ++i) {
+ parametrs[i] = convertFunctions[i].apply(element);
+ }
+ try {
+ output.add(new FinalRow(constructor.newInstance(parametrs), element));
+ } catch (InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException("construct", e);
+ }
+ }
+
+ }
+
+ private Constructor getAskedConstructor() throws CollectionQueryExecuteException {
+ Class>[] outputParametrsTypes = new Class>[convertFunctions.length];
+ for (int i = 0; i < convertFunctions.length; ++i) {
+ outputParametrsTypes[i] = convertFunctions[i].apply(baseCollection.iterator().next()).getClass();
+ }
+ try {
+ return outputClass.getConstructor(outputParametrsTypes);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new CollectionQueryExecuteException("Can not found constructor", e);
+ }
+ }
+
+ private Collection> goodGroups(Collection> table) {
+ List> output = new ArrayList<>();
+ for (FinalRow row : table) {
+ if (groupingCondition == null || groupingCondition.test(row.get())) {
+ output.add(row);
+ }
+ }
+ return output;
+ }
+
+ private void aggregatingGroups(Collection> table) throws CollectionQueryExecuteException {
+ Constructor constructor = getAskedConstructor();
+ Object[] parametrs = new Object[convertFunctions.length];
+ for (FinalRow row : table) {
+ for (int i = 0; i < convertFunctions.length; ++i) {
+ if (convertFunctions[i] instanceof Aggregate) {
+ parametrs[i] = ((Aggregate) convertFunctions[i]).forGroup(row.getFrom());
+ } else {
+ parametrs[i] = convertFunctions[i].apply(row.getAnyFrom());
+ }
+ }
+ try {
+ row.updateRow(constructor.newInstance(parametrs));
+ } catch (InstantiationException | IllegalAccessException
+ | IllegalArgumentException | InvocationTargetException e) {
+ throw new CollectionQueryExecuteException("output class not instantiated", e);
+ }
+ }
+ }
+
+ private Collection convertToFinal(Collection> table) {
+ List output = new ArrayList<>();
+ if (previousPart != null) {
+ for (R row : previousPart) {
+ output.add(row);
+ }
+ }
+ for (FinalRow row : table) {
+ output.add(row.get());
+ }
+ return output;
+ }
+
+ public final Collection execute() throws CollectionQueryExecuteException {
+ Collection> output = null;
+ output = new ArrayList<>();
+
+ try {
+ stream.forEach(new Applier(output, getAskedConstructor()));
+ } catch (RuntimeException e) {
+ if (e.getMessage().equals("constructor")) {
+ throw new CollectionQueryExecuteException("constructor", e.getCause());
+ } else {
+ throw e;
+ }
+ }
+
+ if (groupingFunctions != null) {
+ List> preCalcSortedTable = new ArrayList<>();
+ for (FinalRow element : output) {
+ preCalcSortedTable.add(element);
+ }
+ List>> resultComparators = new ArrayList<>();
+ for (Function> function : groupingFunctions) {
+ resultComparators.add((r1, r2) -> {
+ Comparable result1 = function.apply(r1.getAnyFrom());
+ return result1.compareTo(function.apply(r2.getAnyFrom()));
+ });
+ }
+ Comparator> groupsComparator = getCombinedComparator(resultComparators);
+ preCalcSortedTable.sort(groupsComparator);
+ Collection> groupedTable = new ArrayList<>();
+
+ FinalRow currentGroup = null;
+ for (FinalRow row : preCalcSortedTable) {
+ if (currentGroup != null && groupsComparator.compare(row, currentGroup) == 0) {
+ currentGroup.getFrom().add(row.getAnyFrom());
+ } else {
+ if (currentGroup != null) {
+ groupedTable.add(currentGroup);
+ }
+ currentGroup = row;
+ }
+ }
+ groupedTable.add(currentGroup);
+
+ aggregatingGroups(output);
+ output = goodGroups(groupedTable);
+ }
+ Stream finalOutput = convertToFinal(output).stream();
+ if (finalLimit >= 0) {
+ finalOutput = finalOutput.limit(finalLimit);
+ }
+ if (finalComparator != null) {
+ finalOutput = finalOutput.sorted(finalComparator);
+ }
+ if (isDistinct) {
+ finalOutput = finalOutput.distinct();
+ }
+ return finalOutput.collect(() -> new ArrayList(), (l, e) -> l.add(e), (l1, l2) -> l1.addAll(l2));
+ }
+
+ public final void limit(int limit) throws CollectionQuerySyntaxException {
+ if (limit >= 0) {
+ throw new CollectionQuerySyntaxException("limitation");
+ }
+ finalLimit = limit;
+ }
+
+ @SafeVarargs
+ public final void orderBy(Comparator... comparators) throws CollectionQuerySyntaxException {
+ if (finalComparator != null) {
+ throw new CollectionQuerySyntaxException("ordering functions");
+ }
+ finalComparator = getCombinedComparator(Arrays.asList(comparators));
+ }
+
+ public final Stream stream() throws CollectionQueryExecuteException {
+ return execute().stream();
+ }
+
+ final Stream currentStream() {
+ return stream;
+ }
+
+
+ public final UnionStmt union() throws CollectionQueryExecuteException {
+ return new UnionStmt(this);
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/UnionStmt.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/UnionStmt.java
new file mode 100644
index 00000000..7cd0bd69
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/UnionStmt.java
@@ -0,0 +1,19 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+import java.util.Collection;
+
+public class UnionStmt {
+ private Collection previousPart;
+ private Class previousOutputClass;
+
+ UnionStmt(SelectStmt, R> previousStmt) throws CollectionQueryExecuteException {
+ previousOutputClass = previousStmt.getOutputClass();
+ previousPart = previousStmt.execute();
+ }
+
+ public final FromStmt from(Iterable list) {
+ FromStmt stmt = FromStmt.from(list);
+ stmt.setPreviousPart(previousOutputClass, previousPart);
+ return stmt;
+ }
+}
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/WhereStmt.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/WhereStmt.java
new file mode 100644
index 00000000..864fc159
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/CollectionsQLEx/impl/WhereStmt.java
@@ -0,0 +1,46 @@
+package ru.fizteh.fivt.students.preidman.CollectionsQLEx.impl;
+
+
+import java.util.Comparator;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class WhereStmt {
+
+ private SelectStmt baseStmt;
+
+ WhereStmt(SelectStmt selectStmt) {
+ baseStmt = selectStmt;
+ }
+
+ @SafeVarargs
+ public final WhereStmt groupBy(Function>... expressions)
+ throws CollectionQuerySyntaxException {
+ baseStmt.groupBy(expressions);
+ return this;
+ }
+
+ @SafeVarargs
+ public final WhereStmt orderBy(Comparator... comparators) throws CollectionQuerySyntaxException {
+ baseStmt.orderBy(comparators);
+ return this;
+ }
+
+ public final WhereStmt having(Predicate condition) throws CollectionQuerySyntaxException {
+ baseStmt.having(condition);
+ return this;
+ }
+
+ public final WhereStmt limit(int amount) throws CollectionQuerySyntaxException {
+ baseStmt.limit(amount);
+ return this;
+ }
+
+ public final Iterable execute() throws CollectionQueryExecuteException {
+ return baseStmt.execute();
+ }
+
+ public final UnionStmt union() throws CollectionQueryExecuteException {
+ return baseStmt.union();
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseService.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseService.java
new file mode 100644
index 00000000..147a3986
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseService.java
@@ -0,0 +1,148 @@
+package ru.fizteh.fivt.students.preidman.MiniORM;
+import javafx.util.Pair;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DatabaseService {
+
+ static final String UNNAMED = "";
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public @interface Table {
+ String name() default UNNAMED;
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ public @interface Column {
+ String name() default UNNAMED;
+
+ String type();
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.FIELD)
+ public @interface PrimaryKey {
+ }
+
+ private static final String DATABASE_PATH = "jdbc:h2:./simple_database";
+ private StatementConstructor statementBuilder = null;
+ private String tableName = null;
+ private List columnList = null;
+ private TColumn primaryKey = null;
+ private Class itemsClass;
+
+
+ public DatabaseService(Class newItemsClass) throws IllegalArgumentException {
+ itemsClass = newItemsClass;
+ tableName = DatabaseServiceUtils.getTableName(itemsClass);
+ Pair, TColumn> pair = DatabaseServiceUtils.analyseColumns(itemsClass);
+ columnList = pair.getKey();
+ primaryKey = pair.getValue();
+ statementBuilder = new StatementConstructor(tableName, columnList,
+ primaryKey, itemsClass);
+ }
+
+ public final StatementConstructor getStatementBuilder() {
+ return statementBuilder;
+ }
+
+ @FunctionalInterface
+ public interface CheckedFunction {
+ R apply(T t) throws SQLException, IllegalStateException;
+ }
+
+ // Осуществляет указанное действие с базой данных.
+ private R databaseRequest(CheckedFunction action) {
+ try (Connection connection = DriverManager.getConnection(DATABASE_PATH)) {
+ Statement statement = connection.createStatement();
+ return action.apply(statement);
+ } catch (SQLException e) {
+ throw new IllegalStateException("An SQL error occurred: " + e.getMessage());
+ }
+ }
+
+ public final void createTable() {
+ databaseRequest((Statement statement) -> {
+ statement.execute(statementBuilder.buildCreate());
+ if (primaryKey != null) {
+ statement.execute("ALTER TABLE " + tableName + " ADD PRIMARY KEY (" + primaryKey.getName() + ")");
+ System.err.println("PK успешно добавлен.");
+ }
+ return true;
+ });
+ }
+
+ public final void dropTable() {
+ databaseRequest((Statement statement) -> {
+ statement.execute("DROP TABLE IF EXISTS " + tableName);
+ return true;
+ });
+ }
+
+ public final void insert(T newItem) {
+ int added = databaseRequest(
+ (Statement statement) -> statement.executeUpdate(statementBuilder.buildInsert(newItem))
+ );
+
+ if (added != 0) {
+ System.err.println("Элемент успешно добавлен.");
+ }
+ }
+
+ public final List queryForAll() {
+ List allItems = databaseRequest((Statement statement) -> {
+ ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName);
+ // Обрабатываем полученные результаты.
+ List selectAllList = new ArrayList<>();
+ while (resultSet.next()) {
+ T item = DatabaseServiceUtils.createItemFromSqlResult(resultSet, columnList, itemsClass);
+ selectAllList.add(item);
+ }
+ return selectAllList;
+ });
+ System.err.println("Get all items: " + allItems);
+ return allItems;
+ }
+
+ public final T queryById(K key) {
+ T itemById = databaseRequest((Statement statement) -> {
+ ResultSet resultSet = statement.executeQuery("SELECT * FROM " + tableName
+ + " WHERE " + primaryKey.getName() + "=" + DatabaseServiceUtils.getSqlValue(key));
+ // Обрабатываем полученный результат.
+ T item = null;
+ if (resultSet.next()) {
+ item = DatabaseServiceUtils.createItemFromSqlResult(resultSet, columnList, itemsClass);
+ }
+ if (resultSet.next()) {
+ throw new IllegalStateException("Primary key search result is not single");
+ }
+ return item;
+ });
+ System.err.println("Get item by ID: " + itemById);
+ return itemById;
+ }
+
+ public final void update(T item) {
+ databaseRequest((Statement statement) -> {
+ statement.execute(statementBuilder.buildUpdate(item));
+ return true;
+ });
+ }
+
+ public final void delete(K key) {
+ databaseRequest((Statement statement) -> {
+ statement.execute("DELETE FROM " + tableName + " WHERE "
+ + primaryKey.getName() + "=" + DatabaseServiceUtils.getSqlValue(key));
+ return true;
+ });
+ }
+ }
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseServiceUtils.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseServiceUtils.java
new file mode 100644
index 00000000..7e805d58
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/DatabaseServiceUtils.java
@@ -0,0 +1,156 @@
+package ru.fizteh.fivt.students.preidman.MiniORM;
+import javafx.util.Pair;
+
+import ru.fizteh.fivt.students.preidman.MiniORM.DatabaseService.Column;
+import ru.fizteh.fivt.students.preidman.MiniORM.DatabaseService.PrimaryKey;
+import ru.fizteh.fivt.students.preidman.MiniORM.DatabaseService.Table;
+
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+
+import static java.lang.Character.isUpperCase;
+import static java.lang.Character.toLowerCase;
+
+public class DatabaseServiceUtils {
+
+ public static String camelCaseToLowerCase(String text) {
+
+ StringBuilder lowerCaseText = new StringBuilder("");
+
+ for (int i = 0; i < text.length(); ++i) {
+
+ char ch = text.charAt(i);
+
+ if (isUpperCase(ch)) {
+
+ if (i != 0) lowerCaseText.append("_");
+
+ lowerCaseText.append(toLowerCase(ch));
+
+ } else lowerCaseText.append(ch);
+ }
+
+ return lowerCaseText.toString();
+ }
+
+ public static String getTableName(Class itemClass) throws IllegalArgumentException {
+
+ // Проверяем, проаннотирован ли класс @Table
+ Table tableAnnotation;
+
+ if (itemClass.isAnnotationPresent(Table.class)) {
+
+ tableAnnotation = (Table) itemClass.getAnnotation(Table.class);
+
+ } else throw new IllegalArgumentException("Class has no @Table annotation");
+
+ // Если имя таблицы не указано, то сгерерируем его.
+ String tableName = tableAnnotation.name();
+
+ if (Objects.equals(tableName, DatabaseService.UNNAMED)) {
+
+ tableName = camelCaseToLowerCase(itemClass.getSimpleName());
+
+ }
+
+ return tableName;
+ }
+
+ public static Pair, TColumn> analyseColumns(Class itemClass) {
+
+ List columnList = new ArrayList<>();
+ TColumn primaryKey = null;
+
+ // Пройдемся по полям класса и найдем аннотированные @Column
+ Field[] fields = itemClass.getDeclaredFields();
+
+ for (Field field : fields) {
+ if (field.isAnnotationPresent(Column.class)) {
+
+ Column column = field.getAnnotation(Column.class);
+ String name = column.name();
+ String type = column.type();
+
+ // Если имя не задано, то сгернерируем.
+ if (name.equals(DatabaseService.UNNAMED)) name = camelCaseToLowerCase(field.getName());
+
+ TColumn itemColumn = new TColumn(name, type, field);
+
+ columnList.add(itemColumn);
+
+ if (field.isAnnotationPresent(PrimaryKey.class)) {
+ // Объявление более одного @PrimaryKey недопустимо.
+ if (primaryKey != null) throw new IllegalArgumentException("More than one primary key presents");
+
+ primaryKey = itemColumn;
+ }
+ }
+ }
+ return new Pair, TColumn>(columnList, primaryKey);
+ }
+
+ public static String getSqlValue(T object) {
+ if (object.getClass() == String.class || object.getClass() == char.class) {
+ return "\'" + object.toString() + "\'";
+ } else {
+ return object.toString();
+ }
+ }
+
+ public static T createItemFromSqlResult(ResultSet resultSet,
+ List columnList,
+ Class itemClass) throws SQLException {
+ T newItem = null;
+ try {
+ // Создаем новый объект пустым конструктором.
+ newItem = (T) itemClass.newInstance();
+
+ // Перебираем все нужные столбцы-поля.
+ for (TColumn column : columnList) {
+ Field field = column.getField();
+
+ // Определяем какой тип получать в соостветствии с типом поля.
+ // String
+ if (field.getType() == String.class || field.getType() == char.class) {
+ String value = resultSet.getString(column.getName());
+ field.set(newItem, value);
+ }
+ // int
+ if (field.getType() == int.class || field.getType() == Integer.class) {
+ int value = resultSet.getInt(column.getName());
+ field.set(newItem, value);
+ }
+ // float
+ if (field.getType() == float.class || field.getType() == Double.class) {
+ float value = resultSet.getFloat(column.getName());
+ field.set(newItem, value);
+ }
+ // boolean
+ if (field.getType() == boolean.class) {
+ boolean value = resultSet.getBoolean(column.getName());
+ field.set(newItem, value);
+ }
+ // Date
+ if (field.getType() == Date.class) {
+ Date value = resultSet.getDate(column.getName());
+ field.set(newItem, value);
+ }
+ // Time
+ if (field.getType() == Time.class) {
+ Time value = resultSet.getTime(column.getName());
+ field.set(newItem, value);
+ }
+ }
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return newItem;
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/StatementConstructor.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/StatementConstructor.java
new file mode 100644
index 00000000..92183aa5
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/StatementConstructor.java
@@ -0,0 +1,135 @@
+package ru.fizteh.fivt.students.preidman.MiniORM;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.stream.Collectors.joining;
+
+public class StatementConstructor {
+
+ private String name = null;
+
+ private List columnList = null;
+ private TColumn primaryKey = null;
+
+ private Class itemsClass;
+
+ public StatementConstructor(String newName,
+ List newColumnList,
+ TColumn newPrimaryKey,
+ Class newItemsClass) {
+ this.name = newName;
+ this.columnList = newColumnList;
+ this.primaryKey = newPrimaryKey;
+ this.itemsClass = newItemsClass;
+ }
+
+ public final String buildCreate() {
+
+ StringBuilder createQuery = new StringBuilder();
+
+ createQuery.append("CREATE TABLE IF NOT EXISTS ")
+ .append(name)
+ .append(" (");
+
+ List columns = new ArrayList<>();
+
+ for (TColumn column : columnList) {
+
+ StringBuilder columnsBuilder = new StringBuilder();
+
+ columnsBuilder.append(column.getName())
+ .append(" ")
+ .append(column.getType());
+
+ if (column == primaryKey) columnsBuilder.append(" NOT NULL");
+
+ columns.add(columnsBuilder.toString());
+
+ }
+
+ createQuery.append(columns.stream().collect(joining(", "))).append(")");
+
+ return createQuery.toString();
+ }
+
+ public final String buildInsert(T newItem) {
+
+ StringBuilder insertQuery = new StringBuilder();
+
+ insertQuery.append("INSERT INTO ")
+ .append(name)
+ .append(" VALUES (");
+
+ List columns = new ArrayList<>();
+
+ for (TColumn column : columnList) {
+
+ Field field = column.getField();
+ field.setAccessible(true);
+
+ try {
+
+ columns.add(DatabaseServiceUtils.getSqlValue(field.get(newItem)));
+
+ } catch (IllegalAccessException e) {
+
+ e.printStackTrace();
+
+ }
+ }
+
+ insertQuery.append(columns.stream().collect(joining(", "))).append(")");
+
+ return insertQuery.toString();
+ }
+
+ public final String buildUpdate(T item) {
+
+ StringBuilder updateStatement = new StringBuilder();
+
+ updateStatement.append("UPDATE ")
+ .append(name)
+ .append(" SET ");
+
+ List columns = new ArrayList<>();
+
+ for (TColumn column : columnList) {
+
+ StringBuilder columnBuilder = new StringBuilder();
+ columnBuilder.append(column.getName())
+ .append("=");
+
+ Field field = column.getField();
+ field.setAccessible(true);
+
+ try {
+
+ columnBuilder.append(DatabaseServiceUtils.getSqlValue(field.get(item)));
+
+ } catch (IllegalAccessException e) {
+
+ e.printStackTrace();
+
+ }
+ columns.add(columnBuilder.toString());
+ }
+
+ updateStatement.append(columns.stream().collect(joining(", ")));
+
+ try {
+
+ updateStatement.append(" WHERE ")
+ .append(primaryKey.getName())
+ .append("=")
+ .append(DatabaseServiceUtils.getSqlValue(primaryKey.getField().get(item)));
+
+ } catch (IllegalAccessException e) {
+
+ e.printStackTrace();
+
+ }
+
+ return updateStatement.toString();
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/TColumn.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/TColumn.java
new file mode 100644
index 00000000..4b3fed6f
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/MiniORM/TColumn.java
@@ -0,0 +1,54 @@
+package ru.fizteh.fivt.students.preidman.MiniORM;
+
+import java.lang.reflect.Field;
+
+public class TColumn {
+
+ public TColumn(String newName, String newType, Field newField) {
+
+ this.name = newName;
+ this.type = newType;
+ this.field = newField;
+ }
+
+ @Override
+ public final int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+
+ if (this == obj) return true;
+
+ if (obj == null) return false;
+
+ if (getClass() != obj.getClass()) return false;
+
+ TColumn other = (TColumn) obj;
+
+ return this.name.equals(other.name) && this.type.equals(other.type);
+ }
+
+ public final String getName() {
+
+ return name;
+
+ }
+
+ public final String getType() {
+
+ return type;
+
+ }
+
+ public final Field getField() {
+
+ return field;
+
+ }
+
+ private String name;
+ private String type;
+ private Field field;
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/BlockingQueue.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/BlockingQueue.java
new file mode 100644
index 00000000..7b534a4d
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/BlockingQueue.java
@@ -0,0 +1,124 @@
+package ru.fizteh.fivt.students.preidman.threads;
+
+import java.util.concurrent.*;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.locks.*;
+import java.util.ArrayList;
+
+
+public class BlockingQueue {
+
+ private final int volume;
+ private Lock l;
+
+ private Condition takeCond;
+ private Condition putCond;
+
+ private Queue queueBl;
+
+ public BlockingQueue(int maxSize) {
+
+ volume = maxSize;
+ queueBl = new LinkedList<>();
+
+ l = new ReentrantLock();
+
+ takeCond = l.newCondition();
+ putCond = l.newCondition();
+
+ }
+
+ public final void offer(List newqueueBl) throws InterruptedException, TimeoutException {
+
+ offer(newqueueBl, 0);
+
+ }
+
+ public final void offer(List newqueueBl, long t) throws InterruptedException, TimeoutException {
+
+ l.lock();
+
+ try {
+
+ long time = TimeUnit.MILLISECONDS.toNanos(t);
+
+ boolean isAdd = true;
+
+
+ while ((t == 0 || time > 0) && queueBl.size() + newqueueBl.size() > volume) {
+
+ if (t == 0) putCond.await();
+ else {
+
+ time = putCond.awaitNanos(time);
+ if (time < 0) isAdd = false;
+
+ }
+
+ }
+
+
+ if (isAdd) {
+
+ queueBl.addAll(newqueueBl);
+
+ if (newqueueBl.size() > 0) takeCond.signal();
+
+ } else throw new TimeoutException();
+
+ } finally {
+
+ l.unlock();
+
+ }
+ }
+
+
+ public final List take(int count) throws InterruptedException {
+
+ return take(count, 0);
+
+ }
+
+ public final List take(int kol, long t) throws InterruptedException {
+
+ l.lock();
+
+ try {
+
+ long time = TimeUnit.MILLISECONDS.toNanos(t);
+
+ while (queueBl.size() < kol && (t == 0 || time > 0)) {
+
+ if (t == 0) takeCond.await();
+ else {
+
+ time = takeCond.awaitNanos(time);
+ if (time < 0) return null;
+
+ }
+
+ }
+
+ List tmp = new ArrayList<>();
+
+ for (int i = 0; i < kol; ++i) {
+
+ tmp.add(queueBl.poll());
+
+ }
+
+ if (kol > 0) putCond.signal();
+
+ return tmp;
+
+ } finally {
+
+ l.unlock();
+
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/NumerableThread.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/NumerableThread.java
new file mode 100644
index 00000000..6123247f
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/NumerableThread.java
@@ -0,0 +1,63 @@
+package ru.fizteh.fivt.students.preidman.threads;
+
+
+public class NumerableThread {
+
+ public static void main(String[] args) {
+
+ new NumerableThread(Integer.valueOf(args[0]));
+
+ }
+
+
+
+ private final int kol;
+
+ private Integer par;
+
+
+
+ public class Numerator extends Thread {
+
+ private int num;
+
+ public Numerator(int x) {
+ num = x;
+ }
+
+ @Override
+ public final void run() {
+
+ while (true) {
+
+ synchronized (par) {
+
+ if (par.equals(num - 1)) {
+
+ System.out.println("Thread-" + num);
+ par++;
+
+ if (par.equals(kol)) par = 0;
+
+ }
+ }
+
+ }
+ }
+ }
+
+ public NumerableThread(int numberOfThreads) {
+
+ kol = numberOfThreads;
+
+ par = 0;
+
+ for (int i = 0; i < kol; ++i) {
+
+ Thread count = new Numerator(i + 1);
+ count.start();
+
+ }
+ }
+
+}
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/RollingThread.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/RollingThread.java
new file mode 100644
index 00000000..33c10e4e
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/threads/RollingThread.java
@@ -0,0 +1,170 @@
+package ru.fizteh.fivt.students.preidman.threads;
+
+import java.util.concurrent.Semaphore;
+import java.util.Random;
+
+public class RollingThread implements Runnable {
+
+
+ public static void main(String[] args) {
+
+ new RollingThread (Integer.valueOf(args[0])).run();
+
+ }
+
+
+
+ private int countOfReady;
+
+ private int numberOfThreads;
+ private Responder[] students;
+ private Integer numberOfYes;
+
+ private Semaphore turn;
+ private Boolean end;
+
+
+
+
+
+ public RollingThread (int x) {
+
+ numberOfYes = 0;
+ end = false;
+ numberOfThreads = x;
+
+ turn = new Semaphore(numberOfThreads);
+
+ try {
+
+ turn.acquire(numberOfThreads);
+
+ } catch (InterruptedException e) {
+
+ e.printStackTrace();
+ System.exit(-1);
+
+ }
+
+
+ students = new Responder[numberOfThreads];
+
+ for (int i = 0; i < numberOfThreads; ++i) {
+
+ students[i] = new Responder();
+
+ try {
+
+ students[i].answPls.acquire();
+
+ } catch (InterruptedException e) {
+
+ e.printStackTrace();
+ System.exit(-1);
+
+ }
+ }
+ }
+
+
+
+ @Override
+ public final void run() {
+
+ boolean ready = false;
+
+ for (int i = 0; i < numberOfThreads; ++i) {
+
+ students[i].start();
+
+ }
+
+ while (!ready) {
+
+ System.out.println("Are you ready?");
+ goAnswer();
+
+ try {
+
+ turn.acquire(numberOfThreads);
+ synchronized (numberOfYes) {
+
+ if (countOfReady == numberOfThreads) {
+
+ ready = true;
+ end = true;
+ goAnswer();
+
+ }
+
+ numberOfYes = 0;
+ countOfReady = 0;
+ }
+
+ } catch (InterruptedException e) {
+
+ e.printStackTrace();
+
+ }
+ }
+ }
+
+
+
+ private void goAnswer() {
+
+ for (int i = 0; i < numberOfThreads; ++i) {
+
+ students[i].answPls.release();
+
+ }
+
+ }
+
+ public class Responder extends Thread {
+
+ private Random maybe = new Random();
+
+ private Semaphore answPls = new Semaphore(1);
+
+ @Override
+ public final void run() {
+
+ while (true) {
+
+ try {
+
+ answPls.acquire();
+
+ synchronized (end) {
+
+ if (end) return;
+
+ }
+
+ boolean yes = true;
+
+ if (maybe.nextInt(10) == 0) yes = false;
+
+ if (!yes) System.out.println(getName() + ": No");
+ else System.out.println(getName() + ": Yes");
+
+
+ synchronized (numberOfYes) {
+
+ numberOfYes++;
+ if (yes) countOfReady++;
+
+ }
+
+ turn.release();
+
+ } catch (InterruptedException e) {
+
+ System.err.println(e.getMessage());
+
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Constants.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Constants.java
new file mode 100644
index 00000000..5dc8009d
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Constants.java
@@ -0,0 +1,10 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+public final class Constants {
+ public static final int DEFAULT_LIMIT = 1000;
+ public static final int SLEEP_TIME = 1000;
+ public static final int MAX_COUNT_OF_TRIES = 3;
+ public static final int RADIUS_OF_THE_EARTH = 6371;
+ public static final String URL_FOR_IP_NAVIGATION = "http://ipinfo.io/json";
+ public static final String URL_FOR_GOOGLE_MAPS = "http://maps.googleapis.com/maps/api/geocode/json";
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/DataParser.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/DataParser.java
new file mode 100644
index 00000000..0fc91f7e
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/DataParser.java
@@ -0,0 +1,28 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.time.*;
+
+public class DataParser {
+
+ public String getTime(Date date) {
+ LocalDateTime tweetTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+ LocalDateTime currentTime = LocalDateTime.now();
+ WordMatcher matcher = new WordMatcher();
+ if (tweetTime.isAfter(currentTime.minusMinutes(2))) {
+ return "только что ";
+ } else if (tweetTime.isAfter(currentTime.minusHours(1))) {
+ int minutes = (int) ChronoUnit.MINUTES.between(tweetTime, currentTime);
+ return minutes + " " + matcher.wordForMinutes(minutes) + " назад ";
+ } else if (currentTime.toLocalDate().isEqual(tweetTime.toLocalDate())) {
+ int hours = (int) ChronoUnit.HOURS.between(tweetTime, currentTime);
+ return hours + " " + matcher.wordForHours(hours) + " назад ";
+ } else if (tweetTime.toLocalDate().isEqual(currentTime.minusDays(1).toLocalDate())) {
+ return "вчера ";
+ } else {
+ int days = (int) ChronoUnit.DAYS.between(tweetTime, currentTime);
+ return days + " " + matcher.wordForDays(days) + " назад ";
+ }
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/GeoNavigator.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/GeoNavigator.java
new file mode 100644
index 00000000..712c1ddd
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/GeoNavigator.java
@@ -0,0 +1,37 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import org.json.JSONObject;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Scanner;
+
+public class GeoNavigator {
+
+ public String searchByIP(URL url) throws IOException {
+ Scanner scanner = new Scanner(url.openStream());
+ String response = scanner.useDelimiter("\\Z").next();
+ scanner.close();
+ JSONObject json = new JSONObject(response);
+ return json.getString("city");
+ }
+
+ public Location searchByAddress(URL url) throws IOException {
+ Scanner scanner = new Scanner(url.openStream());
+ String response = scanner.useDelimiter("\\Z").next();
+ scanner.close();
+ JSONObject json = new JSONObject(response);
+ JSONObject geometry = json.getJSONArray("results").getJSONObject(0).getJSONObject("geometry");
+ JSONObject bounds = geometry.getJSONObject("bounds");
+ JSONObject location = geometry.getJSONObject("location");
+ JSONObject northeast = bounds.getJSONObject("northeast");
+ JSONObject southwest = bounds.getJSONObject("southwest");
+ double lat = location.getDouble("lat");
+ double lng = location.getDouble("lng");
+ double northeastLat = northeast.getDouble("lat");
+ double northeastLng = northeast.getDouble("lng");
+ double southwestLat = southwest.getDouble("lat");
+ double southwestLng = southwest.getDouble("lng");
+ return new Location(lat, lng, northeastLat, northeastLng,
+ southwestLat, southwestLng);
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Location.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Location.java
new file mode 100644
index 00000000..159e4679
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/Location.java
@@ -0,0 +1,68 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.RADIUS_OF_THE_EARTH;
+
+public class Location {
+ private double latitude;
+ private double longitude;
+ private double northeastLat;
+ private double northeastLng;
+ private double southwestLat;
+ private double southwestLng;
+
+ Location(double latitude, double longitude,
+ double northeastLat, double northeastLng,
+ double southwestLat, double southwestLng) {
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.northeastLat = northeastLat;
+ this.northeastLng = northeastLng;
+ this.southwestLat = southwestLat;
+ this.southwestLng = southwestLng;
+ }
+
+ public double getLatitude() {
+ return latitude;
+ }
+
+ public double getLongitude() {
+ return longitude;
+ }
+
+ public double getNortheastLat() {
+ return northeastLat;
+ }
+
+ public double getNortheastLng() {
+ return northeastLng;
+ }
+
+ public double getSouthwestLat() {
+ return southwestLat;
+ }
+
+ public double getSouthwestLng() {
+ return southwestLng;
+ }
+
+ public boolean isInBounds(double lat, double lng) {
+ if (lat >= Math.min(southwestLat, northeastLat)
+ && lng > Math.min(southwestLng, northeastLng)
+ && lat <= Math.max(southwestLat, northeastLat)
+ && lng <= Math.max(southwestLng, northeastLng)) {
+ return true;
+ }
+ return false;
+ }
+
+ public double getRadius() {
+ Double latDistance = Math.toRadians(southwestLat - northeastLat);
+ Double lonDistance = Math.toRadians(southwestLng - northeastLng);
+ Double xCoordinte = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
+ + Math.cos(Math.toRadians(southwestLat)) * Math.cos(Math.toRadians(northeastLat))
+ * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
+ Double angle = 2 * Math.atan2(Math.sqrt(xCoordinte), Math.sqrt(1 - xCoordinte));
+ double radius = RADIUS_OF_THE_EARTH * angle / 2;
+ return radius;
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/ParameterParser.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/ParameterParser.java
new file mode 100644
index 00000000..45756e87
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/ParameterParser.java
@@ -0,0 +1,58 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import com.beust.jcommander.Parameter;
+import java.util.ArrayList;
+import java.util.List;
+
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.DEFAULT_LIMIT;
+
+
+public class ParameterParser {
+ @Parameter(names = { "-q", "--query" }, variableArity = true,
+ description = "Ключевые слова для поиска.")
+ private List queries = new ArrayList<>();
+
+ @Parameter(names = { "-p", "--place" },
+ description = "Искать только по заданному региону или по ip, если значение равно nearby.")
+ private String place = null;
+
+ @Parameter(names = { "-s", "--stream" },
+ description = "Выводить результаты поиска равномерно и непрерывно с задержкой в 1 секунду.")
+ private boolean stream = false;
+
+ @Parameter(names = "--hideRetweets",
+ description = "Не выводить ретвиты.")
+ private boolean hideRetweets = false;
+
+ @Parameter(names = { "-l", "--limit" },
+ description = "Выводить только заданное число твитов. Не применимо для --stream режима.")
+ private int limit = DEFAULT_LIMIT;
+
+ @Parameter(names = { "-h", "--help" },
+ description = "Справка.")
+ private boolean help = false;
+
+ public List getQuery() {
+ return queries;
+ }
+
+ public String getPlace() {
+ return place;
+ }
+
+ public boolean isStream() {
+ return stream;
+ }
+
+ public boolean hideRetweets() {
+ return hideRetweets;
+ }
+
+ public int getLimit() {
+ return limit;
+ }
+
+ public boolean isHelp() {
+ return help;
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TweetParser.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TweetParser.java
new file mode 100644
index 00000000..633fabf9
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TweetParser.java
@@ -0,0 +1,26 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import twitter4j.Status;
+
+
+public class TweetParser {
+ public String getTweet(Status status) {
+ String result = "@" + status.getUser().getScreenName() + ": ";
+ String text = status.getText();
+ if (status.isRetweet()) {
+ int index = text.indexOf("@");
+ text = text.substring(index, text.length());
+ index = text.indexOf(" ");
+ String retweetedName = text.substring(0, index - 1);
+ text = text.substring(index + 1, text.length());
+ result += "ретвитнул " + retweetedName + ": ";
+ }
+ result += text;
+ if (!status.isRetweet() && status.getRetweetCount() > 0) {
+ WordMatcher matcher = new WordMatcher();
+ result += " (" + status.getRetweetCount() + " "
+ + matcher.wordForRetweet(status.getRetweetCount()) + ")";
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterService.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterService.java
new file mode 100644
index 00000000..2604d295
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterService.java
@@ -0,0 +1,152 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import twitter4j.*;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.SLEEP_TIME;
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.URL_FOR_GOOGLE_MAPS;
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.URL_FOR_IP_NAVIGATION;
+
+public class TwitterService {
+ private final Twitter twitter;
+ private final TwitterStream twitterStream;
+
+ public TwitterService(Twitter twitter, TwitterStream twitterStream) {
+ this.twitter = twitter;
+ this.twitterStream = twitterStream;
+ }
+
+ public List searchNonStream(ParameterParser parser) throws IOException, TwitterException {
+ String queryString = "";
+ if (!parser.getQuery().isEmpty()) {
+ queryString = String.join(" ", parser.getQuery());
+ }
+ Query query = new Query();
+ if (!queryString.isEmpty()) {
+ query.setQuery(queryString);
+ }
+ Location searchBounds = null;
+ GeoNavigator navigator = new GeoNavigator();
+ String address = parser.getPlace();
+ if (address != null && !address.isEmpty()) {
+ if (address == "nearby") {
+ address = navigator.searchByIP(new URL(URL_FOR_IP_NAVIGATION));
+ }
+ String url = URL_FOR_GOOGLE_MAPS + '?' + "address="
+ + URLEncoder.encode(address, "utf-8") + "&sensor=false";
+ searchBounds = navigator.searchByAddress(new URL(url));
+ if (searchBounds != null) {
+ query.setGeoCode(new GeoLocation(searchBounds.getLatitude(), searchBounds.getLongitude()),
+ searchBounds.getRadius(), Query.Unit.km);
+ }
+ }
+ query.setCount(parser.getLimit());
+ QueryResult result = null;
+ int tweetCount = 0;
+ List statuses = new ArrayList<>();
+ while (tweetCount < parser.getLimit()) {
+ result = twitter.search(query);
+ List tweets = result.getTweets();
+ for (Status tweet : tweets) {
+ if (parser.hideRetweets() && tweet.isRetweet()) {
+ continue;
+ }
+ tweetCount++;
+ statuses.add(tweet);
+ if (tweetCount == parser.getLimit()) {
+ break;
+ }
+ }
+ if (!result.hasNext()) {
+ break;
+ }
+ query = result.nextQuery();
+ }
+ return statuses;
+ }
+
+ public void searchStream(ParameterParser parser, Consumer listener) throws IOException {
+ String[] queries = parser.getQuery().toArray(new String[parser.getQuery().size()]);
+ Location searchBounds = null;
+ GeoNavigator navigator = new GeoNavigator();
+ String address = parser.getPlace();
+ if (address != null && !address.isEmpty()) {
+ if (address == "nearby") {
+ address = navigator.searchByIP(new URL(URL_FOR_IP_NAVIGATION));
+ }
+ String url = URL_FOR_GOOGLE_MAPS + '?' + "address="
+ + URLEncoder.encode(address, "utf-8") + "&sensor=false";
+ searchBounds = navigator.searchByAddress(new URL(url));
+ }
+ final Location bounds = searchBounds;
+ TweetParser tweetParser = new TweetParser();
+ StatusListener statusListener = new StatusAdapter() {
+ @Override
+ public void onStatus(Status status) {
+ if (parser.hideRetweets() && status.isRetweet()) {
+ return;
+ }
+ if (parser.getQuery().size() > 0 && parser.getPlace() != null && !parser.getPlace().isEmpty()) {
+ if (status.getGeoLocation() == null) {
+ return;
+ }
+ double latitude = status.getGeoLocation().getLatitude();
+ double longitude = status.getGeoLocation().getLongitude();
+ if (bounds == null || !bounds.isInBounds(latitude, longitude)) {
+ return;
+ }
+ }
+ listener.accept(status);
+ }
+ };
+ twitterStream.addListener(statusListener);
+ FilterQuery query = null;
+ if (queries.length > 0) {
+ query = new FilterQuery().track(queries);
+ } else if (searchBounds != null) {
+ double[][] boundBox = {{searchBounds.getSouthwestLat(), searchBounds.getSouthwestLng()},
+ {searchBounds.getNortheastLat(), searchBounds.getNortheastLng()}};
+ query = new FilterQuery().locations(boundBox);
+ }
+ if (query == null) {
+ twitterStream.sample();
+ } else {
+ twitterStream.filter(query);
+ }
+ }
+
+ public void printTweets(ParameterParser parser) throws IOException, TwitterException {
+ List tweetList = new ArrayList<>();
+ DataParser dataParser = new DataParser();
+ TweetParser tweetParser = new TweetParser();
+ if (parser.isStream()) {
+ searchStream(parser, new Consumer() {
+ @Override
+ public void accept(Status status) {
+ System.out.println(tweetParser.getTweet(status));
+ try {
+ Thread.sleep(SLEEP_TIME);
+ } catch (InterruptedException exception) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ });
+ } else {
+ tweetList = searchNonStream(parser);
+ if (tweetList.isEmpty()) {
+ System.out.println("No tweets were found.");
+ } else {
+ for (Status status : tweetList) {
+ System.out.print(dataParser.getTime(status.getCreatedAt()));
+ System.out.println(tweetParser.getTweet(status));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterStreamer.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterStreamer.java
new file mode 100644
index 00000000..5d333278
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/TwitterStreamer.java
@@ -0,0 +1,38 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+import com.beust.jcommander.JCommander;
+import twitter4j.*;
+import java.io.IOException;
+
+import static ru.fizteh.fivt.students.preidman.twitterstream.Constants.MAX_COUNT_OF_TRIES;
+
+
+public class TwitterStreamer {
+
+ public static void printHelp(JCommander jCommander) {
+ jCommander.setProgramName("TwitterParser");
+ jCommander.usage();
+ }
+
+ public static void main(String[] args) {
+ ParameterParser parser = new ParameterParser();
+ JCommander jCommander = new JCommander(parser);
+ jCommander.parse(args);
+ Twitter twitter = new TwitterFactory().getInstance();
+ TwitterStream twitterStream = new TwitterStreamFactory().getInstance();
+ TwitterService twitterService = new TwitterService(twitter, twitterStream);
+ int triesCounter = 0;
+ while (triesCounter < MAX_COUNT_OF_TRIES) {
+ try {
+ if (parser.isHelp()) {
+ printHelp(jCommander);
+ } else {
+ twitterService.printTweets(parser);
+ }
+ break;
+ } catch (IOException | TwitterException exception) {
+ triesCounter++;
+ }
+ }
+ }
+}
diff --git a/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/WordMatcher.java b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/WordMatcher.java
new file mode 100644
index 00000000..f468b365
--- /dev/null
+++ b/preidman/src/main/java/ru/fizteh/fivt/students/preidman/twitterstream/WordMatcher.java
@@ -0,0 +1,49 @@
+package ru.fizteh.fivt.students.preidman.twitterstream;
+
+
+public class WordMatcher {
+
+ public String wordForRetweet(int count) {
+ if (count % 100 > 10 && count % 100 < 20) {
+ return "ретвитов";
+ }
+ switch (count % 10) {
+ case 1 : return "ретвит";
+ case 2: case 3: case 4 : return "ретвита";
+ default: return "ретвитов";
+ }
+ }
+
+ public String wordForMinutes(int minutes) {
+ if (minutes % 100 > 10 && minutes % 100 < 20) {
+ return "минут";
+ }
+ switch (minutes % 10) {
+ case 1 : return "минута";
+ case 2: case 3: case 4 : return "минуты";
+ default: return "минут";
+ }
+ }
+
+ public String wordForHours(int hours) {
+ if (hours % 100 > 10 && hours % 100 < 20) {
+ return "часов";
+ }
+ switch (hours % 10) {
+ case 1 : return "час";
+ case 2: case 3: case 4 : return "часа";
+ default: return "часов";
+ }
+ }
+
+ public String wordForDays(int days) {
+ if (days % 100 > 10 && days % 100 < 20) {
+ return "дней";
+ }
+ switch (days % 10) {
+ case 1 : return "день";
+ case 2: case 3: case 4 : return "дня";
+ default: return "дней";
+ }
+ }
+}
\ No newline at end of file