title: Find All URL That Is Not An HTML Attribute or Content of A Hyperlink Tag
body: <p>I'm trying to figure out a regex that matches all URL that are not an attribute of an element or is a content of a hyperlink.</p>
  
  <p>Should match:</p>
  
  <pre><code> 1. This is a url http://www.google.com
  </code></pre>
  
  <p>Should not match:</p>
  
  <pre><code> 1. &lt;a href="http://www.google.com"&gt;Google&lt;/a&gt;
   2. &lt;a href="http://www.google.com"&gt;http://www.google.com&lt;/a&gt; 
   3. &lt;img src="http://www.google.com/image.jpg"&gt;
   4. &lt;div data-url="http://www.google.com"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>I'm currently using this regex to match all URL and I think I know what I have to detect, but I just can't figure out using regex.</p>
  
  <pre><code>\\b(https?|ftp|file)://[-a-zA-Z0-9+&amp;@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&amp;@#/%=~_|]
  </code></pre>
  
  <hr>
  
  <p><strong>EDITED</strong></p>
  
  <p>What I'm trying to achieve is the following. I want to convert this string.</p>
  
  <pre><code>This is a url http://www.google.com &lt;a href="http://www.google.com" title="Go to Google"&gt;Google&lt;/a&gt;&lt;a href="http://www.google.com"&gt;http://www.google.com&lt;/a&gt;&lt;img src="http://www.google.com/image.jpg"&gt;&lt;div data-url="http://www.google.com"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>To</p>
  
  <pre><code>This is a url &lt;a href="http://www.google.com"&gt;http://www.google.com&lt;/a&gt; &lt;a href="http://www.google.com" title="Go to Google"&gt;Google&lt;/a&gt;&lt;a href="http://www.google.com"&gt;http://www.google.com&lt;/a&gt;&lt;img src="http://www.google.com/image.jpg"&gt;&lt;div data-url="http://www.google.com"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>Preprocessing by removing tags and then put them back doesn't solve the problem since actually ends up removing all data attributes of the existing hyperlink elements. It also doesn't solve the issue when there are other URL using in other attributes beside href.</p>
  
  <p>So far, I haven't found a solution suggested by anyone and so far I also haven't found a way to do this using HTML parser. It's actually seem more doable using regex.</p>
  
  <hr>
  
  <p><strong>EDITED 2</strong></p>
  
  <p>After the attempt based on Dean's suggestion, I'm about ready to rule out HTML parser from being able to achieve this for it inability to process string without making it a valid HTML document. Here's the code based on the suggested example + the fix to handle exclusion case 2.</p>
  
  <pre><code>    Document doc = Jsoup.parseBodyFragment(htmlText);
  
      final List&lt;TextNode&gt; nodesToChange = new ArrayList&lt;TextNode&gt;();
  
      NodeTraversor nd  = new NodeTraversor(new NodeVisitor() {
  
          @Override
          public void tail(Node node, int depth) {
              if (node instanceof TextNode) {
                  TextNode textNode = (TextNode) node;
                  Node parent = node.parent();
                  if(parent.nodeName().equals("a")){
                      return;
                  }
  
                  String text = textNode.getWholeText();
  
                  List&lt;String&gt; allMatches = new ArrayList&lt;String&gt;();
                  Matcher m = Pattern.compile("\\b(https?|ftp|file)://[-a-zA-Z0-9+&amp;@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&amp;@#/%=~_|]")
                          .matcher(text);
                  while (m.find()) {
                      allMatches.add(m.group());
                  }
  
                  if(allMatches.size() &gt; 0){
                      nodesToChange.add(textNode);
                  }
              }
          }
  
          @Override
          public void head(Node node, int depth) {        
          }
      });
  
      nd.traverse(doc.body());
  </code></pre>
  
  <p>This code adds HTML, HEAD and BODY tag to the result. The only hack I can think of around this issue  is to check whether HTML, HEAD and BODY tags exist in the string. If not, stripe them out after processing.</p>
  
  <p>I hope someone else has a better suggestion than this hack. Using JSOUP is already very expensive in terms of processing time so I really don't want to add more overhead if I don't have to.</p>