Skip to content

Commit 13d8a55

Browse files
committed
Executive System Touch M5+ 75
1 parent cd09396 commit 13d8a55

4 files changed

Lines changed: 548 additions & 14 deletions

File tree

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* RodPropertyQueryEngine — Queries ALL available data for a given property from
3+
* the Durham County Register of Deeds (rodweb.dconc.gov/web/search/DOCSEARCH5S1).
4+
*
5+
* Searches by parcel ID, PIN, address, and street name to maximize data retrieval.
6+
* Returns all document records (deeds, mortgages, liens, plats, etc.) found for
7+
* the property or its owners.
8+
*
9+
* @author Max Rupplin
10+
* @javaowner Max Rupplin
11+
* @date June 24 2026 EST
12+
*/
13+
14+
package city_analysis;
15+
16+
import javax.net.ssl.*;
17+
import java.io.*;
18+
import java.net.*;
19+
import java.nio.charset.StandardCharsets;
20+
import java.security.SecureRandom;
21+
import java.util.*;
22+
import java.util.regex.*;
23+
24+
public class RodPropertyQueryEngine
25+
{
26+
private static final String ROD_SEARCH_URL = "https://rodweb.dconc.gov/web/search/DOCSEARCH5S1";
27+
private static final String ROD_BASE = "https://rodweb.dconc.gov";
28+
29+
private final String sessionCookie;
30+
31+
public RodPropertyQueryEngine(String sessionCookie)
32+
{
33+
this.sessionCookie = sessionCookie;
34+
}
35+
36+
/**
37+
* Queries all available data for the property using multiple search strategies.
38+
* Tries parcel ID, PIN, street name, and full address to capture all documents.
39+
*
40+
* @return list of pipe-delimited result strings (docType|book|page|grantor|grantee|date|...)
41+
*/
42+
public List<String> queryAllData(String parcelId, String pin, String address, String streetName)
43+
{
44+
Set<String> seen = new HashSet<>();
45+
List<String> allResults = new ArrayList<>();
46+
47+
// Strategy 1: Search by parcel ID (property-based)
48+
if (!parcelId.isEmpty())
49+
{
50+
mergeResults(allResults, seen, querySearch("P", parcelId));
51+
}
52+
53+
// Strategy 2: Search by PIN
54+
if (!pin.isEmpty())
55+
{
56+
mergeResults(allResults, seen, querySearch("P", pin));
57+
}
58+
59+
// Strategy 3: Search by street name (catches all docs on that street)
60+
if (!streetName.isEmpty())
61+
{
62+
mergeResults(allResults, seen, querySearch("A", streetName.trim()));
63+
}
64+
65+
// Strategy 4: Search by full address
66+
if (!address.isEmpty() && !address.equals(streetName))
67+
{
68+
mergeResults(allResults, seen, querySearch("A", address));
69+
}
70+
71+
return allResults;
72+
}
73+
74+
private List<String> querySearch(String category, String criteria)
75+
{
76+
List<String> results = new ArrayList<>();
77+
try
78+
{
79+
String queryUrl = ROD_SEARCH_URL + "?searchCategory=" + category +
80+
"&searchCriteria=" + URLEncoder.encode(criteria, StandardCharsets.UTF_8);
81+
82+
String html = httpGet(queryUrl);
83+
if (html == null || html.isEmpty()) return results;
84+
85+
results = extractResults(html);
86+
}
87+
catch (Exception e) { /* silently skip failed queries */ }
88+
return results;
89+
}
90+
91+
private List<String> extractResults(String html)
92+
{
93+
List<String> results = new ArrayList<>();
94+
95+
// Pattern 1: Table rows with ss-row class
96+
Pattern rowPattern = Pattern.compile(
97+
"<tr[^>]*>(.*?)</tr>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
98+
Matcher rowMatcher = rowPattern.matcher(html);
99+
100+
while (rowMatcher.find())
101+
{
102+
String row = rowMatcher.group(1);
103+
if (!row.contains("<td")) continue;
104+
105+
Pattern cellPattern = Pattern.compile("<td[^>]*>(.*?)</td>", Pattern.DOTALL);
106+
Matcher cellMatcher = cellPattern.matcher(row);
107+
List<String> cells = new ArrayList<>();
108+
while (cellMatcher.find())
109+
{
110+
String cell = cellMatcher.group(1).replaceAll("<[^>]+>", "").trim();
111+
cells.add(cell);
112+
}
113+
if (cells.size() >= 3)
114+
{
115+
results.add(String.join("|", cells));
116+
}
117+
}
118+
119+
// Pattern 2: SelfService list items with document data
120+
if (results.isEmpty())
121+
{
122+
Pattern liPattern = Pattern.compile(
123+
"<li[^>]*data-[^>]*>(.*?)</li>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
124+
Matcher liMatcher = liPattern.matcher(html);
125+
while (liMatcher.find())
126+
{
127+
String item = liMatcher.group(1).replaceAll("<[^>]+>", "").trim();
128+
if (item.length() > 10) results.add(item);
129+
}
130+
}
131+
132+
// Pattern 3: Divs with result class
133+
if (results.isEmpty())
134+
{
135+
Pattern divPattern = Pattern.compile(
136+
"<div[^>]*class=\"[^\"]*result[^\"]*\"[^>]*>(.*?)</div>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
137+
Matcher divMatcher = divPattern.matcher(html);
138+
while (divMatcher.find())
139+
{
140+
String item = divMatcher.group(1).replaceAll("<[^>]+>", "").trim();
141+
if (item.length() > 10) results.add(item);
142+
}
143+
}
144+
145+
return results;
146+
}
147+
148+
private void mergeResults(List<String> all, Set<String> seen, List<String> newResults)
149+
{
150+
for (String r : newResults)
151+
{
152+
if (seen.add(r)) all.add(r);
153+
}
154+
}
155+
156+
private String httpGet(String urlStr) throws Exception
157+
{
158+
URL url = new URL(urlStr);
159+
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
160+
TrustManager[] trustAll = new TrustManager[]{
161+
new X509TrustManager()
162+
{
163+
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
164+
public void checkClientTrusted(java.security.cert.X509Certificate[] c, String a) {}
165+
public void checkServerTrusted(java.security.cert.X509Certificate[] c, String a) {}
166+
}
167+
};
168+
SSLContext ctx = SSLContext.getInstance("TLS");
169+
ctx.init(null, trustAll, new SecureRandom());
170+
conn.setSSLSocketFactory(ctx.getSocketFactory());
171+
conn.setHostnameVerifier((h, s) -> true);
172+
conn.setRequestMethod("GET");
173+
conn.setConnectTimeout(15000);
174+
conn.setReadTimeout(15000);
175+
conn.setRequestProperty("User-Agent", "NitroWebExpress/CityAnalysis 1.0");
176+
if (sessionCookie != null) conn.setRequestProperty("Cookie", sessionCookie);
177+
conn.setInstanceFollowRedirects(true);
178+
179+
int code = conn.getResponseCode();
180+
if (code == 200)
181+
{
182+
try (BufferedReader reader = new BufferedReader(
183+
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)))
184+
{
185+
StringBuilder sb = new StringBuilder();
186+
String l;
187+
while ((l = reader.readLine()) != null) sb.append(l).append("\n");
188+
return sb.toString();
189+
}
190+
}
191+
return null;
192+
}
193+
}

0 commit comments

Comments
 (0)