diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 0000000..f42f140 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,39 @@ +# Change Log + +## Version[0.0.6] - 24-05-2026 +### Fixed +- `Sequence.java`: removed all JPA annotations, now a pure domain object. `value` field changed from comma-separated `String` to `List` directly. Added `setId()` and `setTimestamp()` setters. +- `SequenceHistory.java`: new class that takes over all JPA annotations and DB mapping from `Sequence`. Handles comma-separated conversion of `value` via `fromSequence()` and `toSequence()` methods. +- `SequenceRepo.java`: updated to use `SequenceHistory` instead of `Sequence` as the JPA entity. +- `SequenceService.java`: added `saveAndReturn()` helper to handle `Sequence` ↔ `SequenceHistory` conversion on every save. Added `getAllHistory()` for raw DB access. Added `MAX_Z_CHAIN = 100` constant and full edge case handling in `computeValues()`. +- `SequenceController.java`: added `GET /convert-measurements/history` endpoint that returns raw `SequenceHistory` records from the DB. + +## Version[0.0.5] - 22-05-2026 +### Fixed +- User is able to check all the history records of previous requests. +- Added GET request query for specific IDs. + +## Version[0.0.4] - 22-05-2026 +### Fixed +- Added `SequenceRepo` interface for DB connection. +- Established a connection with the DB and tested with multiple requests. +- PUT request now works with raw JSON format to update input value. + +## Version[0.0.3] - 21-05-2026 +### Fixed +- Added PUT request to edit using input with endpoint `/convert-measurements/{id}` +- Added DELETE request with endpoint `/convert-measurements/{id}`. +- fixed error related to getting source IP rather than ServerSocket used HttpServletRequest. + +## Version[0.0.2] - 21-05-2026 +### Fixed +- User input is converted to lower case. +- Handled invalid user input (numbers and special characters). + +## Version[0.0.1] - 21-05-2026 +### Added +- Initial release of Sequence Measurement API. +- Core algorithm for decoding measurement sequences ("SequenceService") with z-multiplier encoding. +- Spring Boot server with `/convert-measurements` endpoint returning decoded sums. +- Sequence model with auto-generated ID and timestamp. +- Application startup check in `SequenceMeasurementApiApplicationTests.java`. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 20909d2..6c0f7ca 100644 --- a/pom.xml +++ b/pom.xml @@ -1,34 +1,23 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot spring-boot-starter-parent 3.5.14 - + com.oraclequantapi oraclequantapi - 0.0.1-SNAPSHOT - - - - - - - - - - - - - - - + 0.0.1 + oraclequantapi + Sequence Measurement API + 17 + org.springframework.boot @@ -40,6 +29,19 @@ spring-boot-starter-test test + + + + com.oracle.database.jdbc + ojdbc11 + 23.3.0.23.09 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + @@ -47,8 +49,18 @@ org.springframework.boot spring-boot-maven-plugin + + com.oraclequantapi.oraclequantapi.OraclequantapiApplication + + + + + repackage + + + - + \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/README.md b/src/main/java/com/oraclequantapi/oraclequantapi/README.md new file mode 100644 index 0000000..4fde18c --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/README.md @@ -0,0 +1,254 @@ +# Sequence Measurement API + +A Spring Boot REST API that decodes a string encoding format and stores the results in an Oracle XE database. + +--- + +## Encoding Format + +Each input string is made up of back-to-back packages. The encoding rules are: + +- Characters map to values: `_` = 0, `a` = 1, `b` = 2, ... `y` = 25, `z` = 26 +- `z` is the only non-terminating character — it means "add 26 and keep reading" +- Any character that is not `z` terminates the current number +- Each package starts with a count, followed by exactly that many encoded values +- The result of each package is the sum of its decoded values +- The final output is a list of sums, one per package +### Example + +| Input | Decoded | Output | +|-------|---------|--------| +| `abbcc` | Package 1: count=1, values=[2] → Package 2: count=2, values=[3,3] | `[2, 6]` | +| `czzabzc` | Package 1: count=3, values=[53, 2, 29] | `[53, 2, 29]` | +| `dz_a_aazzaaa` | Package 1: count=4, values=[26,1,0,1] → Package 2: count=1, values=[53] → Package 3: count=1, values=[1] | `[28, 53, 1]` | + +--- + +## Architecture + +The project is split into four layers: + +- **`Sequence`** — pure domain object, no JPA annotations, holds business logic and validation +- **`SequenceHistory`** — JPA entity, owns all database mapping, converts to/from `Sequence` via `fromSequence()` and `toSequence()` +- **`SequenceService`** — business logic layer, bridges domain and persistence +- **`SequenceController`** — REST layer, exposes all endpoints +- **`SequenceRepo`** — JPA repository interface for Oracle XE +--- + +## Endpoints + +| Method | URL | Description | +|--------|-----|-------------| +| `GET` | `/convert-measurements?input=abbcc` | Decode a new input string and store the result | +| `GET` | `/convert-measurements/{id}` | Fetch a specific record by ID | +| `GET` | `/convert-measurements/all` | Fetch all records as domain objects | +| `GET` | `/convert-measurements/history` | Fetch all raw records directly from the DB | +| `PUT` | `/convert-measurements/{id}` | Update the input of an existing record and re-decode | +| `DELETE` | `/convert-measurements/{id}` | Delete a specific record by ID | + +### PUT Request Body +```json +{ + "input": "abbcc" +} +``` + +--- + +## Validation + +- Only lowercase letters `a-z` and underscore `_` are allowed in the input +- Capital letters are automatically converted to lowercase +- Any other character (numbers, special characters) returns `"invalid sequence format"` +--- + +## Edge Cases + +The decoder handles the following malformed inputs: + +| Edge Case | Response | +|-----------|----------| +| Zero count package | `"0"` | +| Fewer values than count | `"malformed package: expected X values but found Y"` | +| Unterminated z-chain | `"malformed package: unterminated number"` | +| Z-chain exceeds 100 characters | `"malformed package: number exceeds maximum allowed size"` | +| Single character count with no values | `"malformed package: expected X values but found 0"` | + +--- + +## Setup + +### Prerequisites +- Java 17 +- Spring Boot +- Oracle XE database +- Maven +### `application.properties` +```properties +spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE +spring.datasource.username=your_username +spring.datasource.password=your_password +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true +spring.jpa.open-in-view=false +spring.datasource.hikari.auto-commit=true +``` + +### Database Setup +Run the following in your Oracle XE SQL client: +```sql +CREATE TABLE SEQUENCES ( + ID NUMBER(19) NOT NULL, + INPUT VARCHAR2(4000), + SOURCE_IP VARCHAR2(100), + TIMESTAMP TIMESTAMP, + OUTPUT VARCHAR2(2000), + CONSTRAINT PK_SEQUENCES PRIMARY KEY (ID) +); + +CREATE SEQUENCE SEQUENCE_SEQ + START WITH 1 + INCREMENT BY 1 + NOCACHE + NOCYCLE; +``` + +### Running the API Locally +```bash +mvn spring-boot:run +``` + +The API will be available at `http://localhost:8080` + +--- + +## Deploying as a Service on Oracle Linux + +The API is packaged as `oraclequantapi-0.0.1.jar` and runs as a `systemd` service on Oracle Linux. + +### Prerequisites +- Java 17 installed on the server +- The JAR file transferred to the server +### Step 1 — Verify Java is installed +```bash +java -version +``` +If not installed, run: +```bash +sudo dnf install java-17-openjdk -y +``` + +### Step 2 — Create the application directory +```bash +sudo mkdir -p /opt/oraclequantapi +sudo cp oraclequantapi-0.0.1.jar /opt/oraclequantapi/oraclequantapi-0.0.1.jar +``` + +### Step 3 — Create the systemd service file +```bash +sudo nano /etc/systemd/system/oraclequantapi.service +``` + +Paste the following: +```ini +[Unit] +Description=Oracle Quant API Spring Boot Service +After=network.target + +[Service] +User=root +WorkingDirectory=/opt/oraclequantapi +ExecStart=/usr/bin/java -jar /opt/oraclequantapi/oraclequantapi-0.0.1.jar +SuccessExitStatus=143 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=oraclequantapi +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +### Step 4 — Reload systemd and enable the service +```bash +# Reload systemd to pick up the new service file +sudo systemctl daemon-reload + +# Enable the service to start automatically on boot +sudo systemctl enable oraclequantapi + +# Start the service +sudo systemctl start oraclequantapi +``` + +### Step 5 — Verify the service is running +```bash +sudo systemctl status oraclequantapi +``` + +You should see: +``` +● oraclequantapi.service - Oracle Quant API Spring Boot Service + Loaded: loaded (/etc/systemd/system/oraclequantapi.service; enabled) + Active: active (running) +``` + +### Managing the Service + +| Command | Description | +|---------|-------------| +| `sudo systemctl start oraclequantapi` | Start the service | +| `sudo systemctl stop oraclequantapi` | Stop the service | +| `sudo systemctl restart oraclequantapi` | Restart the service | +| `sudo systemctl status oraclequantapi` | Check service status | +| `sudo systemctl enable oraclequantapi` | Enable on boot | +| `sudo systemctl disable oraclequantapi` | Disable on boot | + +### Viewing Logs +```bash +# View live logs +sudo journalctl -u oraclequantapi -f + +# View last 100 lines +sudo journalctl -u oraclequantapi -n 100 +``` + +--- + +## Example Requests + +### Decode a new sequence +``` +GET http://localhost:8080/convert-measurements?input=abbcc +``` +Response: +```json +["2", "6"] +``` + +### Update an existing sequence +``` +PUT http://localhost:8080/convert-measurements/1 +``` +Body: +```json +{ + "input": "czzabzc" +} +``` +Response: +```json +["84"] +``` + +### Delete a sequence +``` +DELETE http://localhost:8080/convert-measurements/1 +``` +Response: +``` +Sequence 1 deleted successfully +``` \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/controllers/SequenceController.java b/src/main/java/com/oraclequantapi/oraclequantapi/controllers/SequenceController.java new file mode 100644 index 0000000..cead4d5 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/controllers/SequenceController.java @@ -0,0 +1,84 @@ +package com.oraclequantapi.oraclequantapi.controllers; + + +import com.oraclequantapi.oraclequantapi.models.Sequence; +import com.oraclequantapi.oraclequantapi.models.SequenceHistory; +import com.oraclequantapi.oraclequantapi.services.SequenceService; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + +@RestController +public class SequenceController { + + private final SequenceService sequenceService; + + public SequenceController(SequenceService sequenceService) { + this.sequenceService = sequenceService; + } + + @GetMapping("/convert-measurements") + public ResponseEntity> convertMeasurements(@RequestParam String input, HttpServletRequest request) { + String clientIP = getClientIP(request); + Sequence sequence = sequenceService.decode(input, clientIP); + if (!sequence.isValid()) { + return ResponseEntity.badRequest().body(List.of("invalid sequence format")); + } + return ResponseEntity.ok(sequence.getValue()); + } + + @GetMapping("/convert-measurements/{id}") + public ResponseEntity getMeasurementById(@PathVariable long id) { + Sequence sequence = sequenceService.getById(id); + if (sequence == null) return ResponseEntity.notFound().build(); + return ResponseEntity.ok(sequence); + } + + @GetMapping("/convert-measurements/all") + public ResponseEntity> getAllMeasurements() { + List all = sequenceService.getAll(); + if (all.isEmpty()) return ResponseEntity.noContent().build(); + return ResponseEntity.ok(all); + } + + // Returns raw persistence entities. Intentionally bypasses the domain + // conversion — see SequenceService.getAllHistory(). + @GetMapping("/convert-measurements/history") + public ResponseEntity> getHistory() { + List history = sequenceService.getAllHistory(); + if (history.isEmpty()) return ResponseEntity.noContent().build(); + return ResponseEntity.ok(history); + } + + @PutMapping("/convert-measurements/{id}") + public ResponseEntity> updateMeasurement(@PathVariable long id, @RequestBody Map body) { + String input = body.get("input"); + if (input == null || input.isEmpty()) return ResponseEntity.badRequest().body(List.of("input field is required")); + Sequence updated = sequenceService.update(id, input); + if (updated == null) return ResponseEntity.notFound().build(); + if (!updated.isValid()) return ResponseEntity.badRequest().body(List.of("invalid sequence format")); + return ResponseEntity.ok(updated.getValue()); + } + + @DeleteMapping("/convert-measurements/{id}") + public ResponseEntity deleteMeasurement(@PathVariable long id) { + boolean deleted = sequenceService.delete(id); + if (!deleted) return ResponseEntity.notFound().build(); + return ResponseEntity.ok("Sequence " + id + " deleted successfully"); + } + + private String getClientIP(HttpServletRequest request) { + String forwarded = request.getHeader("X-Forwarded-For"); + if (forwarded != null && !forwarded.isEmpty()) return forwarded.split(",")[0].trim(); + return request.getRemoteAddr(); + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/models/Sequence.java b/src/main/java/com/oraclequantapi/oraclequantapi/models/Sequence.java new file mode 100644 index 0000000..be16aa6 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/models/Sequence.java @@ -0,0 +1,41 @@ +package com.oraclequantapi.oraclequantapi.models; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +public class Sequence { + + private Long id; + private LocalDateTime timestamp = LocalDateTime.now(); + private String input; + private String sourceIP; + private List value = new ArrayList<>(); + + public Sequence() {} + + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + + public LocalDateTime getTimestamp() { return timestamp; } + public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; } + + public String getInput() { return input; } + public void setInput(String input) { this.input = input; } + + public String getSourceIP() { return sourceIP; } + public void setSourceIP(String sourceIP) { this.sourceIP = sourceIP; } + + public List getValue() { + return value == null ? new ArrayList<>() : value; + } + + public void setValue(List value) { + this.value = value; + } + + public boolean isValid() { + if (input == null || input.isEmpty()) return false; + return input.matches("[a-z_]+"); + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/models/SequenceHistory.java b/src/main/java/com/oraclequantapi/oraclequantapi/models/SequenceHistory.java new file mode 100644 index 0000000..19a673f --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/models/SequenceHistory.java @@ -0,0 +1,84 @@ +package com.oraclequantapi.oraclequantapi.models; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +@Entity +@Table(name = "SEQUENCES") +public class SequenceHistory { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen") + @SequenceGenerator(name = "seq_gen", sequenceName = "SEQUENCE_SEQ", allocationSize = 1) + private Long id; + + @Column(name = "TIMESTAMP") + private LocalDateTime timestamp; + + @Column(name = "OUTPUT") + private String value; + + @Column(name = "INPUT") + private String input; + + @Column(name = "SOURCE_IP") + private String sourceIP; + + public SequenceHistory() {} + + public static SequenceHistory fromSequence(Sequence sequence) { + SequenceHistory history = new SequenceHistory(); + history.id = sequence.getId(); + history.timestamp = sequence.getTimestamp(); + history.input = sequence.getInput(); + history.sourceIP = sequence.getSourceIP(); + + List values = sequence.getValue(); + if (values == null || values.isEmpty()) { + history.value = ""; + } else { + // Manual join to keep this stream/lambda-free. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < values.size(); i++) { + if (i > 0) sb.append(","); + sb.append(values.get(i)); + } + history.value = sb.toString(); + } + return history; + } + + public Sequence toSequence() { + Sequence sequence = new Sequence(); + sequence.setId(this.id); + sequence.setTimestamp(this.timestamp); + sequence.setInput(this.input); + sequence.setSourceIP(this.sourceIP); + + if (this.value == null || this.value.isEmpty()) { + sequence.setValue(new ArrayList()); + } else { + // Arrays.asList returns a fixed-size list; wrap in ArrayList so callers can mutate. + sequence.setValue(new ArrayList(Arrays.asList(this.value.split(",")))); + } + return sequence; + } + + public Long getId() { return id; } + public LocalDateTime getTimestamp() { return timestamp; } + public String getValue() { return value; } + public String getInput() { return input; } + public String getSourceIP() { return sourceIP; } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/repositories/SequenceRepo.java b/src/main/java/com/oraclequantapi/oraclequantapi/repositories/SequenceRepo.java new file mode 100644 index 0000000..5ee2b74 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/repositories/SequenceRepo.java @@ -0,0 +1,10 @@ +package com.oraclequantapi.oraclequantapi.repositories; + + +import com.oraclequantapi.oraclequantapi.models.SequenceHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SequenceRepo extends JpaRepository { +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/services/SequenceService.java b/src/main/java/com/oraclequantapi/oraclequantapi/services/SequenceService.java new file mode 100644 index 0000000..2037508 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/services/SequenceService.java @@ -0,0 +1,208 @@ +package com.oraclequantapi.oraclequantapi.services; + + +import com.oraclequantapi.oraclequantapi.models.Sequence; +import com.oraclequantapi.oraclequantapi.models.SequenceHistory; +import com.oraclequantapi.oraclequantapi.repositories.SequenceRepo; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class SequenceService { + + // Hard cap on a single number's z-chain length. 100 consecutive z's would + // already be 2600+, which is well beyond any plausible legitimate input. + private static final int MAX_Z_CHAIN = 100; + + private final SequenceRepo sequenceRepo; + + public SequenceService(SequenceRepo sequenceRepo) { + this.sequenceRepo = sequenceRepo; + } + + public Sequence decode(String input, String sourceIP) { + Sequence sequence = new Sequence(); + sequence.setInput(input == null ? null : input.toLowerCase()); + sequence.setSourceIP(sourceIP); + + if (!sequence.isValid()) { + List err = new ArrayList(); + err.add("invalid sequence format"); + sequence.setValue(err); + return saveAndReturn(sequence); + } + + sequence.setValue(computeValues(sequence.getInput())); + return saveAndReturn(sequence); + } + + public List getAll() { + List rows = sequenceRepo.findAll(); + List out = new ArrayList(); + for (SequenceHistory row : rows) { + out.add(row.toSequence()); + } + return out; + } + + /** + * Returns persistence entities directly, with no domain conversion. + * Useful for raw inspection/debugging. Note: this leaks the JPA mapping + * shape to whoever calls it — caller beware. + */ + public List getAllHistory() { + return sequenceRepo.findAll(); + } + + public Sequence getById(long id) { + SequenceHistory row = sequenceRepo.findById(id).orElse(null); + if (row == null) return null; + return row.toSequence(); + } + + public Sequence update(long id, String newInput) { + SequenceHistory existing = sequenceRepo.findById(id).orElse(null); + if (existing == null) return null; + + Sequence sequence = existing.toSequence(); + sequence.setInput(newInput == null ? null : newInput.toLowerCase()); + + if (!sequence.isValid()) { + List err = new ArrayList(); + err.add("invalid sequence format"); + sequence.setValue(err); + return saveAndReturn(sequence); + } + + sequence.setValue(computeValues(sequence.getInput())); + return saveAndReturn(sequence); + } + + public boolean delete(long id) { + if (!sequenceRepo.existsById(id)) return false; + sequenceRepo.deleteById(id); + return true; + } + + // Saves the domain Sequence by converting to SequenceHistory, then maps + // the persisted id back onto the domain object so the caller sees the + // generated id without needing a second conversion. + private Sequence saveAndReturn(Sequence sequence) { + SequenceHistory saved = sequenceRepo.save(SequenceHistory.fromSequence(sequence)); + sequence.setId(saved.getId()); + return sequence; + } + + /** + * Decode the input into a list of per-package sums. + * Any malformed package short-circuits processing: the error message is + * appended to the result list and decoding stops. + */ + private List computeValues(String normalized) { + List results = new ArrayList(); + int i = 0; + + while (i < normalized.length()) { + // ---- Read the package count ---- + int count = 0; + int zChain = 0; + boolean countTerminated = false; + + while (i < normalized.length()) { + char c = normalized.charAt(i++); + if (c == 'z') { + zChain++; + // Edge case: very large z-chain in the count. + // Caps runaway / abusive inputs. Same cap applies to value reads below. + if (zChain > MAX_Z_CHAIN) { + results.add("malformed package: number exceeds maximum allowed size"); + return results; + } + count += 26; + } else { + count += charToValue(c); + countTerminated = true; + break; + } + } + + // Edge case: trailing z with no terminator (also covers "input is only z's + // with no terminator anywhere" — that's just this case when it triggers on + // the very first package). + if (!countTerminated) { + results.add("malformed package: unterminated number"); + return results; + } + + // Edge case: zero count. An underscore as the count, or any sequence + // of characters summing to zero (only '_' can do this since 'z' adds 26), + // produces an empty package whose sum is 0. This also covers the + // "underscore as count giving zero packages" case — it's the same code path. + if (count == 0) { + results.add("0"); + continue; + } + + // ---- Read 'count' values and sum them ---- + int sum = 0; + int valuesRead = 0; + + for (int j = 0; j < count; j++) { + if (i >= normalized.length()) { + // Ran out of input mid-package. Handled below after the loop. + break; + } + + int value = 0; + int valueZChain = 0; + boolean valueTerminated = false; + + while (i < normalized.length()) { + char c = normalized.charAt(i++); + if (c == 'z') { + valueZChain++; + // Edge case: very large z-chain inside a value. + if (valueZChain > MAX_Z_CHAIN) { + results.add("malformed package: number exceeds maximum allowed size"); + return results; + } + value += 26; + } else { + value += charToValue(c); + valueTerminated = true; + break; + } + } + + // Edge case: a value's z-chain never terminated (input ended mid-number). + if (!valueTerminated) { + results.add("malformed package: unterminated number"); + return results; + } + + sum += value; + valuesRead++; + } + + // Edge case: fewer values than count. Covers two of your listed cases: + // - "fewer values than count" (general) + // - "single character that is only a count with no values" (specific: + // count > 0, valuesRead == 0) + if (valuesRead < count) { + results.add("malformed package: expected " + count + " values but found " + valuesRead); + return results; + } + + results.add(String.valueOf(sum)); + } + + return results; + } + + private int charToValue(char c) { + if (c == '_') return 0; + return c - 'a' + 1; + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 99d0060..4d5455b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,10 @@ spring.application.name=oraclequantapi +#ip of windows device +spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/XEPDB1 +spring.datasource.username=system +spring.datasource.password=29999login +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +spring.jpa.database-platform=org.hibernate.dialect.OracleDialect +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true