use of com.codename1.io.URL in project CodenameOne by codenameone.
the class ConnectionRequest method performOperation.
/**
* Performs the actual network request on behalf of the network manager
*/
void performOperation() throws IOException {
if (shouldStop()) {
return;
}
if (cacheMode == CachingMode.OFFLINE) {
InputStream is = getCachedData();
if (is != null) {
readResponse(is);
Util.cleanup(is);
} else {
responseCode = 404;
throw new IOException("File unavilable in cache");
}
return;
}
CodenameOneImplementation impl = Util.getImplementation();
Object connection = null;
input = null;
output = null;
redirecting = false;
try {
String actualUrl = createRequestURL();
if (timeout > 0) {
connection = impl.connect(actualUrl, isReadRequest(), isPost() || isWriteRequest(), timeout);
} else {
connection = impl.connect(actualUrl, isReadRequest(), isPost() || isWriteRequest());
}
if (shouldStop()) {
return;
}
initConnection(connection);
if (httpMethod != null) {
impl.setHttpMethod(connection, httpMethod);
}
if (isCookiesEnabled()) {
Vector v = impl.getCookiesForURL(actualUrl);
if (v != null) {
int c = v.size();
if (c > 0) {
StringBuilder cookieStr = new StringBuilder();
Cookie first = (Cookie) v.elementAt(0);
cookieSent(first);
cookieStr.append(first.getName());
cookieStr.append("=");
cookieStr.append(first.getValue());
for (int iter = 1; iter < c; iter++) {
Cookie current = (Cookie) v.elementAt(iter);
cookieStr.append(";");
cookieStr.append(current.getName());
cookieStr.append("=");
cookieStr.append(current.getValue());
cookieSent(current);
}
impl.setHeader(connection, cookieHeader, initCookieHeader(cookieStr.toString()));
} else {
String s = initCookieHeader(null);
if (s != null) {
impl.setHeader(connection, cookieHeader, s);
}
}
} else {
String s = initCookieHeader(null);
if (s != null) {
impl.setHeader(connection, cookieHeader, s);
}
}
}
if (checkSSLCertificates && canGetSSLCertificates()) {
sslCertificates = getSSLCertificatesImpl(connection, url);
checkSSLCertificates(sslCertificates);
if (shouldStop()) {
return;
}
}
if (isWriteRequest()) {
progress = NetworkEvent.PROGRESS_TYPE_OUTPUT;
output = impl.openOutputStream(connection);
if (shouldStop()) {
return;
}
if (NetworkManager.getInstance().hasProgressListeners() && output instanceof BufferedOutputStream) {
((BufferedOutputStream) output).setProgressListener(this);
}
if (requestBody != null) {
if (shouldWriteUTFAsGetBytes()) {
output.write(requestBody.getBytes("UTF-8"));
} else {
OutputStreamWriter w = new OutputStreamWriter(output, "UTF-8");
w.write(requestBody);
}
} else {
buildRequestBody(output);
}
if (shouldStop()) {
return;
}
if (output instanceof BufferedOutputStream) {
((BufferedOutputStream) output).flushBuffer();
if (shouldStop()) {
return;
}
}
}
timeSinceLastUpdate = System.currentTimeMillis();
responseCode = impl.getResponseCode(connection);
if (isCookiesEnabled()) {
String[] cookies = impl.getHeaderFields("Set-Cookie", connection);
if (cookies != null && cookies.length > 0) {
ArrayList cook = new ArrayList();
int clen = cookies.length;
for (int iter = 0; iter < clen; iter++) {
Cookie coo = parseCookieHeader(cookies[iter]);
if (coo != null) {
cook.add(coo);
cookieReceived(coo);
}
}
impl.addCookie((Cookie[]) cook.toArray(new Cookie[cook.size()]));
}
}
if (responseCode == 304 && cacheMode != CachingMode.OFF) {
cacheUnmodified();
return;
}
if (responseCode - 200 < 0 || responseCode - 200 > 100) {
readErrorCodeHeaders(connection);
// redirect to new location
if (followRedirects && (responseCode == 301 || responseCode == 302 || responseCode == 303 || responseCode == 307)) {
String uri = impl.getHeaderField("location", connection);
if (!(uri.startsWith("http://") || uri.startsWith("https://"))) {
// relative URI's in the location header are illegal but some sites mistakenly use them
url = Util.relativeToAbsolute(url, uri);
} else {
url = uri;
}
if (requestArguments != null && url.indexOf('?') > -1) {
requestArguments.clear();
}
if ((responseCode == 302 || responseCode == 303)) {
if (this.post && shouldConvertPostToGetOnRedirect()) {
this.post = false;
setWriteRequest(false);
}
}
impl.cleanup(output);
impl.cleanup(connection);
connection = null;
output = null;
if (!onRedirect(url)) {
redirecting = true;
retry();
}
return;
}
responseErrorMessge = impl.getResponseMessage(connection);
handleErrorResponseCode(responseCode, responseErrorMessge);
if (!isReadResponseForErrors()) {
return;
}
}
responseContentType = getHeader(connection, "Content-Type");
if (cacheMode == CachingMode.SMART || cacheMode == CachingMode.MANUAL) {
String last = getHeader(connection, "Last-Modified");
String etag = getHeader(connection, "ETag");
Preferences.set("cn1MSince" + createRequestURL(), last);
Preferences.set("cn1Etag" + createRequestURL(), etag);
}
readHeaders(connection);
contentLength = impl.getContentLength(connection);
timeSinceLastUpdate = System.currentTimeMillis();
progress = NetworkEvent.PROGRESS_TYPE_INPUT;
if (isReadRequest()) {
input = impl.openInputStream(connection);
if (shouldStop()) {
return;
}
if (input instanceof BufferedInputStream) {
if (NetworkManager.getInstance().hasProgressListeners()) {
((BufferedInputStream) input).setProgressListener(this);
}
((BufferedInputStream) input).setYield(getYield());
}
if (!post && cacheMode == CachingMode.SMART && destinationFile == null && destinationStorage == null) {
byte[] d = Util.readInputStream(input);
OutputStream os = FileSystemStorage.getInstance().openOutputStream(getCacheFileName());
os.write(d);
os.close();
readResponse(new ByteArrayInputStream(d));
} else {
readResponse(input);
}
if (shouldAutoCloseResponse()) {
input.close();
}
input = null;
}
} finally {
// always cleanup connections/streams even in case of an exception
impl.cleanup(output);
impl.cleanup(input);
impl.cleanup(connection);
timeSinceLastUpdate = -1;
input = null;
output = null;
connection = null;
}
if (!isKilled()) {
Display.getInstance().callSerially(new Runnable() {
public void run() {
postResponse();
}
});
}
}
use of com.codename1.io.URL in project CodenameOne by codenameone.
the class SEBrowserComponent method init.
private static void init(SEBrowserComponent self, BrowserComponent p) {
final WeakReference<SEBrowserComponent> weakSelf = new WeakReference<>(self);
final WeakReference<BrowserComponent> weakP = new WeakReference<>(p);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SEBrowserComponent self = weakSelf.get();
if (self == null) {
return;
}
self.cnt = new InternalJPanel(self.instance, self);
// <--- Important if container is opaque it will cause
self.cnt.setOpaque(false);
// all kinds of flicker due to painting conflicts with CN1 pipeline.
self.cnt.setLayout(new BorderLayout());
self.cnt.add(BorderLayout.CENTER, self.panel);
// cnt.setVisible(false);
}
});
self.web.getEngine().getLoadWorker().messageProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
SEBrowserComponent self = weakSelf.get();
BrowserComponent p = weakP.get();
if (self == null || p == null) {
return;
}
if (t1.startsWith("Loading http:") || t1.startsWith("Loading file:") || t1.startsWith("Loading https:")) {
String url = t1.substring("Loading ".length());
if (!url.equals(self.currentURL)) {
p.fireWebEvent("onStart", new ActionEvent(url));
}
self.currentURL = url;
} else if ("Loading complete".equals(t1)) {
}
}
});
self.web.getEngine().setOnAlert(new EventHandler<WebEvent<String>>() {
@Override
public void handle(WebEvent<String> t) {
BrowserComponent p = weakP.get();
if (p == null) {
return;
}
String msg = t.getData();
if (msg.startsWith("!cn1_message:")) {
System.out.println("Receiving message " + msg);
p.fireWebEvent("onMessage", new ActionEvent(msg.substring("!cn1_message:".length())));
}
}
});
self.web.getEngine().getLoadWorker().exceptionProperty().addListener(new ChangeListener<Throwable>() {
@Override
public void changed(ObservableValue<? extends Throwable> ov, Throwable t, Throwable t1) {
System.out.println("Received exception: " + t1.getMessage());
if (ov.getValue() != null) {
ov.getValue().printStackTrace();
}
if (t != ov.getValue() && t != null) {
t.printStackTrace();
}
if (t1 != ov.getValue() && t1 != t && t1 != null) {
t.printStackTrace();
}
}
});
self.web.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue ov, State oldState, State newState) {
SEBrowserComponent self = weakSelf.get();
BrowserComponent p = weakP.get();
try {
netscape.javascript.JSObject w = (netscape.javascript.JSObject) self.web.getEngine().executeScript("window");
if (w == null) {
System.err.println("Could not get window");
} else {
Bridge b = new Bridge(p);
self.putClientProperty("SEBrowserComponent.Bridge.jconsole", b);
w.setMember("jconsole", b);
}
} catch (Throwable t) {
Log.e(t);
}
if (self == null || p == null) {
return;
}
String url = self.web.getEngine().getLocation();
if (newState == State.SCHEDULED) {
p.fireWebEvent("onStart", new ActionEvent(url));
} else if (newState == State.RUNNING) {
p.fireWebEvent("onLoadResource", new ActionEvent(url));
} else if (newState == State.SUCCEEDED) {
if (!p.isNativeScrollingEnabled()) {
self.web.getEngine().executeScript("document.body.style.overflow='hidden'");
}
// let's just add a client property to the BrowserComponent to enable firebug
if (Boolean.TRUE.equals(p.getClientProperty("BrowserComponent.firebug"))) {
self.web.getEngine().executeScript("if (!document.getElementById('FirebugLite')){E = document['createElement' + 'NS'] && document.documentElement.namespaceURI;E = E ? document['createElement' + 'NS'](E, 'script') : document['createElement']('script');E['setAttribute']('id', 'FirebugLite');E['setAttribute']('src', 'https://getfirebug.com/' + 'firebug-lite.js' + '#startOpened');E['setAttribute']('FirebugLite', '4');(document['getElementsByTagName']('head')[0] || document['getElementsByTagName']('body')[0]).appendChild(E);E = new Image;E['setAttribute']('src', 'https://getfirebug.com/' + '#startOpened');}");
}
netscape.javascript.JSObject window = (netscape.javascript.JSObject) self.web.getEngine().executeScript("window");
Bridge b = new Bridge(p);
self.putClientProperty("SEBrowserComponent.Bridge.cn1application", b);
window.setMember("cn1application", b);
self.web.getEngine().executeScript("while (window._cn1ready && window._cn1ready.length > 0) {var f = window._cn1ready.shift(); f();}");
// System.out.println("cn1application is "+self.web.getEngine().executeScript("window.cn1application && window.cn1application.shouldNavigate"));
self.web.getEngine().executeScript("window.addEventListener('unload', function(e){console.log('unloading...');return 'foobar';});");
p.fireWebEvent("onLoad", new ActionEvent(url));
}
self.currentURL = url;
self.repaint();
}
});
self.web.getEngine().getLoadWorker().exceptionProperty().addListener(new ChangeListener<Throwable>() {
@Override
public void changed(ObservableValue<? extends Throwable> ov, Throwable t, Throwable t1) {
BrowserComponent p = weakP.get();
if (p == null) {
return;
}
t1.printStackTrace();
if (t1 == null) {
if (t == null) {
p.fireWebEvent("onError", new ActionEvent("Unknown error", -1));
} else {
p.fireWebEvent("onError", new ActionEvent(t.getMessage(), -1));
}
} else {
p.fireWebEvent("onError", new ActionEvent(t1.getMessage(), -1));
}
}
});
// Monitor the location property so that we can send the shouldLoadURL event.
// This allows us to cancel the loading of a URL if we want to handle it ourself.
self.web.getEngine().locationProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> prop, String before, String after) {
SEBrowserComponent self = weakSelf.get();
BrowserComponent p = weakP.get();
if (self == null || p == null) {
return;
}
if (!p.fireBrowserNavigationCallbacks(self.web.getEngine().getLocation())) {
self.web.getEngine().getLoadWorker().cancel();
}
}
});
self.adjustmentListener = new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
Display.getInstance().callSerially(new Runnable() {
public void run() {
SEBrowserComponent self = weakSelf.get();
if (self == null) {
return;
}
self.onPositionSizeChange();
}
});
}
};
}
use of com.codename1.io.URL in project CodenameOne by codenameone.
the class BrowserComponent method fireBrowserNavigationCallbacks.
/**
* Fires all of the registered browser navigation callbacks against the provided URL.
* @param url The URL to fire the navigation callbacks against.
* @return True if all of the callbacks say that they can browse. False otherwise.
*/
public boolean fireBrowserNavigationCallbacks(String url) {
boolean shouldNavigate = true;
if (browserNavigationCallback != null && !browserNavigationCallback.shouldNavigate(url)) {
shouldNavigate = false;
}
if (browserNavigationCallbacks != null) {
for (BrowserNavigationCallback cb : browserNavigationCallbacks) {
if (!cb.shouldNavigate(url)) {
shouldNavigate = false;
}
}
}
if (!url.startsWith("javascript:") && url.indexOf(RETURN_URL_PREFIX) != -1) {
// System.out.println("Received browser navigation callback "+url);
String result = decodeURL(url.substring(url.indexOf(RETURN_URL_PREFIX) + RETURN_URL_PREFIX.length()), "UTF-8");
// System.out.println("After decode "+result);
Result structResult = Result.fromContent(result, Result.JSON);
int callbackId = structResult.getAsInteger("callbackId");
final String value = structResult.getAsString("value");
final String type = structResult.getAsString("type");
final String errorMessage = structResult.getAsString("errorMessage");
final SuccessCallback<JSRef> callback = popReturnValueCallback(callbackId);
if (jsCallbacks != null && jsCallbacks.contains(callback)) {
// If this is a registered callback, then we treat it more like
// an event listener, and we retain it for future callbacks.
returnValueCallbacks.put(callbackId, callback);
}
if (callback != null) {
if (errorMessage != null) {
if (fireCallbacksOnEdt) {
Display.getInstance().callSerially(new Runnable() {
public void run() {
if (callback instanceof Callback) {
((Callback) callback).onError(this, new RuntimeException(errorMessage), 0, errorMessage);
}
}
});
} else {
if (callback instanceof Callback) {
((Callback) callback).onError(this, new RuntimeException(errorMessage), 0, errorMessage);
}
}
} else {
if (fireCallbacksOnEdt) {
Display.getInstance().callSerially(new Runnable() {
public void run() {
callback.onSucess(new JSRef(value, type));
}
});
} else {
callback.onSucess(new JSRef(value, type));
}
}
} else {
Log.e(new RuntimeException("Received return value from javascript, but no callback could be found for that ID"));
}
shouldNavigate = false;
}
return shouldNavigate;
}
use of com.codename1.io.URL in project CodenameOne by codenameone.
the class CN1CSSCLI method compile.
private static void compile(File inputFile, File outputFile) throws IOException {
File baseDir = inputFile.getParentFile().getParentFile();
File checksumsFile = getChecksumsFile(baseDir);
if (!checksumsFile.exists()) {
saveChecksums(baseDir, new HashMap<String, String>());
}
if (!checksumsFile.exists()) {
throw new RuntimeException("Failed to create checksums file");
}
FileChannel channel = new RandomAccessFile(checksumsFile, "rw").getChannel();
FileLock lock = channel.lock();
try {
Map<String, String> checksums = loadChecksums(baseDir);
if (outputFile.exists() && !isMavenProject(inputFile)) {
String outputFileChecksum = getMD5Checksum(outputFile.getAbsolutePath());
String previousChecksum = checksums.get(inputFile.getName());
if (previousChecksum == null || !previousChecksum.equals(outputFileChecksum)) {
File backups = new File(inputFile.getParentFile(), ".backups");
backups.mkdirs();
File bak = new File(backups, outputFile.getName() + "." + System.currentTimeMillis() + ".bak");
Files.copy(outputFile.toPath(), bak.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.out.println(outputFile + " has been modified since it was last compiled. Making copy at " + bak);
outputFile.delete();
}
}
if (outputFile.exists() && inputFile.lastModified() <= outputFile.lastModified()) {
System.out.println("File has not changed since last compile.");
return;
}
try {
URL url = inputFile.toURI().toURL();
// CSSTheme theme = CSSTheme.load(CSSTheme.class.getResource("test.css"));
CSSTheme theme = CSSTheme.load(url);
theme.cssFile = inputFile;
theme.resourceFile = outputFile;
JavaSEPort.setBaseResourceDir(outputFile.getParentFile());
WebViewProvider webViewProvider = new WebViewProvider() {
@Override
public BrowserComponent getWebView() {
if (web == null) {
if (!CN1Bootstrap.isCEFLoaded()) /* && !CN1Bootstrap.isJavaFXLoaded()*/
{
// to output the correct bounds so we are killing FX support. CEF only.
throw new MissingNativeBrowserException();
}
if (!CN.isEdt()) {
CN.callSerially(() -> {
getWebView();
});
int counter = 0;
while (web == null && counter++ < 50) {
Util.sleep(100);
}
return web;
}
web = new BrowserComponent();
ComponentSelector.select("*", web).add(web, true).selectAllStyles().setBgTransparency(0).setMargin(0).setPadding(0).setBorder(Border.createEmpty()).each(new ComponentClosure() {
@Override
public void call(Component c) {
c.setOpaque(false);
}
});
web.setOpaque(false);
Form f = new Form();
f.getContentPane().getStyle().setBgColor(0xff0000);
f.getContentPane().getStyle().setBgTransparency(0xff);
if (f.getToolbar() == null) {
f.setToolbar(new com.codename1.ui.Toolbar());
}
f.getToolbar().hideToolbar();
f.setLayout(new com.codename1.ui.layouts.BorderLayout());
f.add(CN.CENTER, web);
f.show();
}
return web;
}
};
File cacheFile = new File(theme.cssFile.getParentFile(), theme.cssFile.getName() + ".checksums");
if (outputFile.exists() && cacheFile.exists()) {
theme.loadResourceFile();
theme.loadSelectorCacheStatus(cacheFile);
}
theme.createImageBorders(webViewProvider);
theme.updateResources();
theme.save(outputFile);
theme.saveSelectorChecksums(cacheFile);
String checksum = getMD5Checksum(outputFile.getAbsolutePath());
checksums.put(inputFile.getName(), checksum);
saveChecksums(baseDir, checksums);
} catch (MalformedURLException ex) {
Logger.getLogger(CN1CSSCLI.class.getName()).log(Level.SEVERE, null, ex);
}
} finally {
if (lock != null) {
lock.release();
}
if (channel != null) {
channel.close();
}
}
}
use of com.codename1.io.URL in project CodenameOne by codenameone.
the class GenerateArchetypeFromTemplateMojo method generateBaseArchetype.
private File generateBaseArchetype(String string, File templateFile) throws TemplateParseException, IOException {
Dependency out = new Dependency();
String archetype = extractSectionFrom(string, "archetype");
Properties props = new Properties();
props.load(new StringReader(archetype));
String[] requiredProperties = new String[] { "artifactId", "groupId", "version" };
for (String key : requiredProperties) {
if (!props.containsKey(key)) {
throw new TemplateParseException("archetype property " + key + " required and missing. Make sure it is defined in the [archetype] section of the template");
}
}
File dest = new File(outputDir, props.getProperty("artifactId"));
if (dest.exists()) {
if (overwrite) {
FileUtils.deleteDirectory(dest);
} else {
throw new IOException("Project already exists at " + dest + ". Delete this project before regenerating");
}
}
String base = props.getProperty("extends", null);
if (base == null) {
throw new TemplateParseException("[archetype] section requires the 'extends' property to specify the path to the archetype project that this extends");
}
baseArchetypeDir = new File(base);
if (!baseArchetypeDir.isAbsolute()) {
baseArchetypeDir = new File(templateFile.getParentFile(), base);
}
if (!baseArchetypeDir.exists()) {
throw new IOException("Cannot find archetype project that this template extends. Looking for it in " + baseArchetypeDir);
}
if (!new File(baseArchetypeDir, "pom.xml").exists()) {
throw new IOException("Base archetype directory " + baseArchetypeDir + " is not a maven project.");
}
FileUtils.copyDirectory(baseArchetypeDir, dest);
File pomFile = new File(dest, "pom.xml");
String groupId = null;
String artifactId = null;
String version = null;
if (props.containsKey("id")) {
String id = props.getProperty("id");
String[] parts = id.split(":");
if (parts.length != 3) {
throw new TemplateParseException("Failed ot parse id property in [archetype] section. It should be in the format groupId:artifactId:version");
}
groupId = parts[0];
artifactId = parts[1];
version = parts[2];
}
groupId = props.getProperty("groupId", groupId);
artifactId = props.getProperty("artifactId", artifactId);
version = props.getProperty("version", version);
if (groupId == null || artifactId == null || version == null) {
throw new TemplateParseException("The [archetype] section is required, and must have at least groupId, artifactId, and version defined. You may also define these using the id property in the format groupId:artifactId:version");
}
String parentTag = "";
String parentGroupId = null;
String parentArtifactId = null;
String parentVersion = null;
if (props.containsKey("parent")) {
String parent = props.getProperty("parent");
String[] parts = parent.split(":");
if (parts.length != 3) {
throw new TemplateParseException("Failed to parse parent property in [archetype] section. It should be in the format groupId:artifactId:version");
}
parentGroupId = parts[0];
parentArtifactId = parts[1];
parentVersion = parts[2];
}
parentGroupId = props.getProperty("parentGroupId", parentGroupId);
parentArtifactId = props.getProperty("parentArtifactId", parentArtifactId);
parentVersion = props.getProperty("parentVersion", parentVersion);
if (parentGroupId != null && parentVersion != null && parentArtifactId != null) {
parentTag = " <parent>\n" + " <groupId>" + parentGroupId + "</groupId>\n" + " <artifactId>" + parentArtifactId + "</artifactId>\n" + " <version>" + parentVersion + "</version>\n" + " </parent>\n";
}
String pomContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" + " <modelVersion>4.0.0</modelVersion>\n" + parentTag + " <groupId>" + groupId + "</groupId>\n" + " <artifactId>" + artifactId + "</artifactId>\n" + " <version>" + version + "</version>\n" + " <packaging>maven-archetype</packaging>\n" + "\n" + " <name>" + artifactId + "</name>\n" + "\n" + "\n" + " <build>\n" + " <extensions>\n" + " <extension>\n" + " <groupId>org.apache.maven.archetype</groupId>\n" + " <artifactId>archetype-packaging</artifactId>\n" + " <version>3.2.0</version>\n" + " </extension>\n" + " </extensions>\n" + "\n" + " <pluginManagement>\n" + " <plugins>\n" + " <plugin>\n" + " <artifactId>maven-archetype-plugin</artifactId>\n" + " <version>3.2.0</version>\n" + " </plugin>\n" + " </plugins>\n" + " </pluginManagement>\n" + " </build>\n" + "\n" + " <description>Artifact generated using the cn1:generate-archetype goal</description>\n" + "\n" + " <url>https://www.codenameone.com</url>\n" + "\n" + " <licenses>\n" + " <license>\n" + " <name>GPL v2 With Classpath Exception</name>\n" + " <url>https://openjdk.java.net/legal/gplv2+ce.html</url>\n" + " <distribution>repo</distribution>\n" + " <comments>A business-friendly OSS license</comments>\n" + " </license>\n" + " </licenses>\n" + "</project>\n";
FileUtils.writeStringToFile(pomFile, pomContents, "UTF-8");
return dest;
}
Aggregations