Skip to content

Commit c7a935b

Browse files
authored
Add tools API and errors package (#4)
* Add tools API and errors package * Delegate `TextLines` to the `Lines()` method * Refactor `IndexOutOfBoundsError` and `IllegalArgumentError` * Refactor `calculateTextLines` to ignore rune (int32) * Refactor `String()` to drop the empty‐slice guard * Return -1 instead of 0 on error * Use `any` type instead of `interface{}` * Add Copilot prompt file * Use overflow-safe midpoint calc * Use `string` instead of `[]rune` * Address code review suggestions
1 parent 15b5e5f commit c7a935b

23 files changed

+1545
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Task
2+
3+
Convert the selected Java source into idiomatic Go **with minimal semantic drift**.
4+
5+
- Map packages to directories and package names.
6+
- Map classes (including abstract) and their fields/methods to Go **interface** and **unexported implementation structs**.
7+
- Preserve logic while adapting to Go naming, encapsulation and error handling.
8+
- Keep diffs focused: one Java file -> one Go file (small support code if strictly necessary).
9+
10+
## Inputs
11+
12+
- Java file(s) to convert.
13+
- Any related types the file depends on.
14+
- Package path target within the Go module.
15+
16+
## Outputs
17+
18+
- A new Go file with idiomatic code.
19+
20+
## Package & File Mapping
21+
22+
1. **One-to-one element mapping**
23+
- **Files**: Map each Java source file `<Name>.java` to to one go file, named `lowercase-with-dashes.go` (e.g., `FooBar.java` -> `foo-bar.go`). Be consistent within the package.
24+
- **Classes/Interfaces**: For each Java class or interface define a **Go interface** named after the Java type (exported) and an **unexported implementation struct**.
25+
- **Methods**: Map each java method to a Go method or function. For overloads, pick distinct names (e.g., `Advance`, `AdvanceN`).
26+
27+
2. **Package structure**
28+
- Java: `package com.example.foo.something;`
29+
- Go: directory `foo/something` with `package something` in the file.
30+
31+
## Types & Encapsulation
32+
33+
### Classes -> Interface + Impl Struct
34+
35+
- Define an **interface** named exactly after the Java class, e.g. `Foo`.
36+
- Define a constructor `NewFoo(...) Foo` returning the interface.
37+
- Define an **unexported** struct `fooImpl` implements the interface. Prefer composition/embedding for reuse.
38+
39+
### Fields -> Struct Fields
40+
41+
- Private/encapsulated state lives in unexported struct fields (e.g., `bar int`).
42+
- Provide getters/setters as interface methods only if the Java API requires them to be public. Avoid exporting fields directly unless they are immutable configuration.
43+
44+
### Constructors
45+
46+
For each Java public constructor:
47+
48+
```go
49+
func NewFoo(params) Foo {
50+
return &fooImpl{/* initialize fields */}
51+
}
52+
```
53+
54+
- If multiple Java constructors exist, use either distinct names (`NewFooWithParams`) **or** the functional options pattern for optional params (avoid overloading).
55+
56+
### Methods & Receivers
57+
58+
- **Non-mutating** -> value receiver if the struct is small and the method is read-only.
59+
- **Mutating** -> pointer receiver.
60+
- Prefer returning `(T, error)` over panicking; translate Java exceptions to `error` values when they cross API boundaries.
61+
62+
### Abstract classes
63+
64+
```go
65+
type Foo interface {
66+
// abstract methods + requires accessors
67+
}
68+
69+
type fooBase struct {/* shared fields */}
70+
71+
func (b fooBase) GetX() T { return b.x }
72+
73+
type fooImpl struct {
74+
fooBase
75+
}
76+
func NewFoo(params) Foo {
77+
return &fooImpl{
78+
fooBase: fooBase{/* initialize shared fields */},
79+
}
80+
}
81+
```
82+
83+
### Method overloading
84+
85+
Java:
86+
87+
```java
88+
void advance();
89+
void advance(int n);
90+
```
91+
92+
Go:
93+
94+
```go
95+
func (r *readerImpl) Advance() {}
96+
func (r *readerImpl) AdvanceN(n int) {}
97+
```
98+
99+
### Equality / Hashing
100+
101+
- If Java only overrides `equals()`/`hashCode()`, in Go prefer:
102+
- Use direct `==` for comparable structs; or
103+
- Provide an explicit **lookup key**:
104+
105+
```go
106+
type FooLookupKey struct { Bar int; Baz string }
107+
func (f *fooImpl) FooLookupKey() FooLookupKey { return FooLookupKey{f.bar, f.baz} }
108+
```
109+
110+
## Naming & Comments
111+
112+
- **Packages:** short, lowercase, single word (`something`).
113+
- **Exports:** capitalize to export. Keep names concise and avoid stutter (prefer `something.Reader` with type name `Reader`).
114+
115+
## Error Handling
116+
117+
- Always return `(T, error)` for fallible operations. Don't use `panic` for normal control flow.
118+
- Wrap lower-level errors with context using `fmt.Errorf("op: %w", err)` so callers can use `errors.Is/As`.
119+
- Match errors with `errors.Is` (sentinels) or `errors.As` (typed errors with additional context).
120+
121+
### Java exceptions -> Go typed errors
122+
123+
- Define Java-specific exceptions as typed errros in `common/errors/errors.go` (package `errors`).
124+
125+
```go
126+
package errors
127+
128+
import "fmt"
129+
130+
type IndexOutOfBoundsError struct {
131+
index int
132+
length int
133+
}
134+
135+
func (e IndexOutOfBoundsError) Error() string {
136+
return fmt.Sprintf("Index %d out of bounds for length %d", e.index, e.length)
137+
}
138+
139+
func (e IndexOutOfBoundsError) GetIndex() int { return e.index }
140+
func (e IndexOutOfBoundsError) GetLength() int { return e.length }
141+
```
142+
143+
## Generics
144+
145+
- Map Java generics to Go generics when needed.
146+
147+
## Guardrails & Non goals
148+
149+
- **Do no** add file headers or license comments.
150+
- **Do not** introduce new public APIs unless requires by the Java surface.
151+
- **Do not** add comments unless the Java source has them.

common/errors/errors.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package errors
20+
21+
import "fmt"
22+
23+
type IndexOutOfBoundsError struct {
24+
index int
25+
length int
26+
}
27+
28+
func (e IndexOutOfBoundsError) Error() string {
29+
return fmt.Sprintf("Index %d out of bounds for length %d", e.index, e.length)
30+
}
31+
32+
func (e IndexOutOfBoundsError) GetIndex() int {
33+
return e.index
34+
}
35+
36+
func (e IndexOutOfBoundsError) GetLength() int {
37+
return e.length
38+
}
39+
40+
func NewIndexOutOfBoundsError(index, length int) *IndexOutOfBoundsError {
41+
return &IndexOutOfBoundsError{
42+
index: index,
43+
length: length,
44+
}
45+
}
46+
47+
type IllegalArgumentError struct {
48+
argument any
49+
}
50+
51+
func (e IllegalArgumentError) Error() string {
52+
return fmt.Sprintf("Illegal argument: %v", e.argument)
53+
}
54+
55+
func (e IllegalArgumentError) GetArgument() any {
56+
return e.argument
57+
}
58+
59+
func NewIllegalArgumentError(argument any) *IllegalArgumentError {
60+
return &IllegalArgumentError{
61+
argument: argument,
62+
}
63+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package diagnostics
20+
21+
import (
22+
"fmt"
23+
24+
"ballerina-lang-go/tools/text"
25+
)
26+
27+
// DefaultDiagnostic is an internal implementation of the Diagnostic interface that is used by the DiagnosticFactory
28+
// to create diagnostics.
29+
type DefaultDiagnostic interface {
30+
Diagnostic
31+
}
32+
33+
type defaultDiagnosticImpl struct {
34+
diagnosticBase
35+
diagnosticInfo DiagnosticInfo
36+
location Location
37+
properties []DiagnosticProperty[any]
38+
message string
39+
}
40+
41+
func NewDefaultDiagnostic(diagnosticInfo DiagnosticInfo, location Location, properties []DiagnosticProperty[any], args ...any) DefaultDiagnostic {
42+
message := formatMessage(diagnosticInfo.MessageFormat(), args...)
43+
return &defaultDiagnosticImpl{
44+
diagnosticInfo: diagnosticInfo,
45+
location: location,
46+
properties: properties,
47+
message: message,
48+
}
49+
}
50+
51+
func (dd *defaultDiagnosticImpl) Location() Location {
52+
return dd.location
53+
}
54+
55+
func (dd *defaultDiagnosticImpl) DiagnosticInfo() DiagnosticInfo {
56+
return dd.diagnosticInfo
57+
}
58+
59+
func (dd *defaultDiagnosticImpl) Message() string {
60+
return dd.message
61+
}
62+
63+
func (dd *defaultDiagnosticImpl) Properties() []DiagnosticProperty[any] {
64+
return dd.properties
65+
}
66+
67+
func (dd *defaultDiagnosticImpl) String() string {
68+
lineRange := dd.location.LineRange()
69+
filePath := lineRange.FileName()
70+
71+
startLine := lineRange.StartLine()
72+
endLine := lineRange.EndLine()
73+
74+
oneBasedStartLine := text.LinePositionFromLineAndOffset(startLine.Line()+1, startLine.Offset()+1)
75+
oneBasedEndLine := text.LinePositionFromLineAndOffset(endLine.Line()+1, endLine.Offset()+1)
76+
oneBasedLineRange := text.LineRangeFromLinePositions(filePath, oneBasedStartLine, oneBasedEndLine)
77+
78+
return fmt.Sprintf("%s [%s:%s] %s",
79+
dd.diagnosticInfo.Severity().String(),
80+
filePath,
81+
oneBasedLineRange.String(),
82+
dd.Message())
83+
}
84+
85+
func formatMessage(format string, args ...any) string {
86+
if len(args) == 0 {
87+
return format
88+
}
89+
return fmt.Sprintf(format, args...)
90+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package diagnostics
20+
21+
// DiagnosticCode represents a diagnostic code.
22+
// Diagnostic code uniquely identifies a diagnostic.
23+
type DiagnosticCode interface {
24+
Severity() DiagnosticSeverity
25+
DiagnosticId() string
26+
MessageKey() string
27+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package diagnostics
20+
21+
// CreateDiagnostic creates a Diagnostic instance from the given details.
22+
//
23+
// Parameters:
24+
// - diagnosticInfo: static diagnostic information
25+
// - location: the location of the diagnostic
26+
// - args: arguments to diagnostic message format
27+
//
28+
// Returns a Diagnostic instance.
29+
func CreateDiagnostic(diagnosticInfo DiagnosticInfo, location Location, args ...any) Diagnostic {
30+
return NewDefaultDiagnostic(diagnosticInfo, location, nil, args...)
31+
}
32+
33+
// CreateDiagnosticWithProperties creates a Diagnostic instance from the given details.
34+
//
35+
// Parameters:
36+
// - diagnosticInfo: static diagnostic information
37+
// - location: the location of the diagnostic
38+
// - properties: properties associated with the diagnostic
39+
// - args: arguments to diagnostic message format
40+
//
41+
// Returns a Diagnostic instance.
42+
func CreateDiagnosticWithProperties(diagnosticInfo DiagnosticInfo, location Location, properties []DiagnosticProperty[any], args ...any) Diagnostic {
43+
return NewDefaultDiagnostic(diagnosticInfo, location, properties, args...)
44+
}

0 commit comments

Comments
 (0)