Skip to content
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# au_software_design

##1. Shell

###Command line utility similar to Unix shell.

####Supported features:

* echo command
* cat command
* wc command
* exit command
* pwd command
* environment variables
* unknown commands are passed to system shell as separate process through Java.Process library.

####Class diagram:
![shell class diagram](https://www.gliffy.com/go/share/image/smx5dub0j39jxied850w.png?utm_medium=live-embed&utm_source=custom)

####Data flow:
* Main: run Shell object.
* Shell: Read line from System.in.

* Preprocessor: Substitute environment variables in input string. E. g. "Hello, $name" -> "Hello, Alex"
* Tokeniser: Split string into a list of tokens: words and operators.
* Parser: Parse list of tokens as sequence of commands divided by pipes.
* Command Executor: Perform chained computation passing output of one comand as input to the next one.
* Pass result to System.in and loop again.
1 change: 0 additions & 1 deletion shell/.gradle/2.10/taskArtifacts/cache.properties

This file was deleted.

Binary file removed shell/.gradle/2.10/taskArtifacts/cache.properties.lock
Binary file not shown.
Binary file removed shell/.gradle/2.10/taskArtifacts/fileHashes.bin
Binary file not shown.
Binary file removed shell/.gradle/2.10/taskArtifacts/fileSnapshots.bin
Binary file not shown.
Binary file not shown.
Binary file removed shell/.gradle/2.10/taskArtifacts/taskArtifacts.bin
Binary file not shown.
1 change: 0 additions & 1 deletion shell/.gradle/3.1/taskArtifacts/cache.properties

This file was deleted.

Binary file removed shell/.gradle/3.1/taskArtifacts/cache.properties.lock
Binary file not shown.
Binary file removed shell/.gradle/3.1/taskArtifacts/fileHashes.bin
Binary file not shown.
Binary file removed shell/.gradle/3.1/taskArtifacts/fileSnapshots.bin
Binary file not shown.
Binary file removed shell/.gradle/3.1/taskArtifacts/taskArtifacts.bin
Binary file not shown.
6 changes: 6 additions & 0 deletions shell/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ group 'simiyutin'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'application'
mainClassName = "com.simiyutin.au.shell.core.Main"

sourceCompatibility = 1.8

repositories {
mavenCentral()
}

run{
standardInput = System.in
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}
54 changes: 54 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Cat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.io.IOException;
import java.nio.charset.MalformedInputException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Scanner;

/**
* Reads file from parameter to stdout or reflects stdin to stdout
*/
public class Cat extends Command {

public static final String NAME = "cat";

public Cat(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream stream) throws CommandExecutionException {

if (stream.hasNext()) {
return stream;
}

Stream res = new Stream();
if (args.isEmpty()) {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println(sc.nextLine());
}
} else {
String filename = args.get(0);
Path file = Paths.get(filename);
try {
List<String> lines = Files.readAllLines(file);
lines.forEach(res::write);
} catch (MalformedInputException e) {
throw new CommandExecutionException("Cannot determine file encoding");
} catch (IOException e) {
throw new CommandExecutionException("Error while reading file: " + e.toString());
}
}
return res;
}
}
33 changes: 33 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Echo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.util.List;
import java.util.stream.Collectors;


/**
* Prints arguments to output stream
*/
public class Echo extends Command {

public static final String NAME = "echo";

public Echo(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream ignored) throws CommandExecutionException {

String toPrint = args.stream().collect(Collectors.joining());

Stream stream = new Stream();
stream.write(toPrint);
return stream;
}

}
26 changes: 26 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Eq.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.util.List;

/**
* Puts new variable in context or changes variable value
*/
public class Eq extends Command {

public Eq(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream ignored) throws CommandExecutionException {
String var = args.get(0);
String val = args.get(1);
env.put(var, val);
return new Stream();
}
}
26 changes: 26 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Exit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.util.List;

/**
* Exits shell
*/
public class Exit extends Command {

public static final String NAME = "exit";

public Exit(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream stream) throws CommandExecutionException {
System.exit(0);
return null;
}
}
59 changes: 59 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/OutSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.io.*;
import java.util.List;
import java.util.stream.Collectors;

/**
* Passes input arguments and stream to external shell and reads result from its stdout
*/
public class OutSource extends Command {

private String commandName;

public OutSource(String commandName, List<String> args, Environment env) {
super(args, env);
this.commandName = commandName;
}

@Override
public Stream run(Stream stream) throws CommandExecutionException {
String command = commandName + " " + args.stream().collect(Collectors.joining(" "));
Stream output = new Stream();
try {
Process process = Runtime.getRuntime().exec(command);
write(process, stream);
output = read(process);
} catch (IOException | InterruptedException e) {
throw new CommandExecutionException("System error: " + e.toString());
}
return output;
}

private void write(Process process, Stream stream) throws IOException {
Writer handle = new PrintWriter(process.getOutputStream());
final int lineFeed = 10;
while (stream.hasNext()) {
handle.write(stream.read());
handle.write(lineFeed);
}
handle.close();
}

private Stream read(Process process) throws IOException, InterruptedException {
Stream output = new Stream();
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.write(line);
}
process.waitFor();
return output;
}
}
31 changes: 31 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Pwd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
* Prints current directory
*/
public class Pwd extends Command {

public static final String NAME = "pwd";

public Pwd(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream ignored) throws CommandExecutionException {
Path currentRelativePath = Paths.get("");
String currentDir = currentRelativePath.toAbsolutePath().toString();
Stream stream = new Stream();
stream.write(currentDir);
return stream;
}
}
88 changes: 88 additions & 0 deletions shell/src/main/java/com/simiyutin/au/shell/commands/Wc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.simiyutin.au.shell.commands;

import com.simiyutin.au.shell.core.exceptions.CommandExecutionException;
import com.simiyutin.au.shell.core.Command;
import com.simiyutin.au.shell.core.Environment;
import com.simiyutin.au.shell.core.Stream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

/**
* Counts lines, words and bytes in given file or input stream
*/
public class Wc extends Command {

public static final String NAME = "wc";

public Wc(List<String> args, Environment env) {
super(args, env);
}

@Override
public Stream run(Stream stream) throws CommandExecutionException {
if (!args.isEmpty()) {
return handleFile();
} else if (stream.hasNext()) {
return handleInputStream(stream);
} else {
handleStdIn();
return new Stream();
}
}

private Stream handleFile() {
String fileName = args.get(0);
Path file = Paths.get(fileName);
try {
List<String> lines = Files.readAllLines(file);
return parseLines(lines);
} catch (IOException e) {
e.printStackTrace();
return new Stream();
}
}

private Stream handleInputStream(Stream stream) {
List<String> lines = new ArrayList<>();
while (stream.hasNext()) {
lines.add(stream.read());
}
return parseLines(lines);
}

private void handleStdIn() {
Scanner sc = new Scanner(System.in);
while (true) {
List<String> lines = Collections.singletonList(sc.nextLine());
Stream result = parseLines(lines);
System.out.println(result.read());
}
}

private Stream parseLines(List<String> lines) {
int linesCount = lines.size();
char[] concatenated = lines.stream().collect(Collectors.joining(" ")).toCharArray();
long wordCount = 0;
for (char c : concatenated) {
if (c == ' ') {
wordCount++;
}
}
wordCount++;

long byteCount = concatenated.length;

String result = String.format(" %d %d %d", linesCount, wordCount, byteCount);
Stream stream = new Stream();
stream.write(result);
return stream;
}
}
Loading