//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.epf.common.utils.FileUtil;
import org.eclipse.epf.common.utils.NetUtil;
import org.eclipse.epf.common.utils.StrUtil;
import org.eclipse.epf.common.utils.XMLUtil;
import org.eclipse.epf.common.xml.XSLTProcessor;
import org.eclipse.epf.library.ILibraryManager;
import org.eclipse.epf.library.LibraryPlugin;
import org.eclipse.epf.library.LibraryService;
import org.eclipse.epf.library.configuration.ConfigurationHelper;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.library.layout.DefaultContentValidator;
import org.eclipse.epf.library.layout.IContentValidator;
import org.eclipse.epf.library.layout.LayoutResources;
import org.eclipse.epf.library.layout.LinkInfo;
import org.eclipse.epf.library.layout.util.XmlElement;
import org.eclipse.epf.library.layout.util.XmlHelper;
import org.eclipse.epf.persistence.MethodLibraryPersister;
import org.eclipse.epf.persistence.MultiFileResourceSetImpl;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.MethodElement;
import org.eclipse.epf.uma.MethodPlugin;
import org.eclipse.epf.uma.util.UmaUtil;

/**
 * @author Jinhua Xi
 * @since 1.0
 */
public class ResourceHelper {

	// public static final String BRACE_REPLACEMENT = "_BR_"; //$NON-NLS-1$
	// public static final String OPENBRACE_STRING = "\\{"; //$NON-NLS-1$

	public static final String URL_BOOKMARK_INDICATOR = "#"; //$NON-NLS-1$

	public static final String URL_PARAMETER_INDICATOR = "?"; //$NON-NLS-1$

	public static final String URL_PARAMETER_PROCESS = "proc"; //$NON-NLS-1$

	public static final String URL_STR_JAVASCRIPT = "javascript:"; //$NON-NLS-1$

	public static final String URL_STR_MAILTO = "mailto:"; //$NON-NLS-1$

	public static final String URL_PARAMETER_PATH = "path"; //$NON-NLS-1$

	public static final String TAG_ATTR_NAME = "name"; //$NON-NLS-1$

	public static final String TAG_ATTR_KEY = "key"; //$NON-NLS-1$

	public static final String TAG_ATTR_TEXT = "text"; //$NON-NLS-1$

	public static final String TAG_ATTR_CLASS = "class"; //$NON-NLS-1$

	public static final String TAG_ATTR_GUID = "guid"; //$NON-NLS-1$

	public static final String TAG_ATTR_HREF = "href"; //$NON-NLS-1$

	public static final String TAG_ATTR_VALUE_INDEX = "index"; //$NON-NLS-1$

	public static final String FILE_EXT_HTML = ".html"; //$NON-NLS-1$

	public static final String FILE_EXT_HTM = ".htm"; //$NON-NLS-1$

	public static final String FILE_EXT_JPEG = ".jpeg"; //$NON-NLS-1$

	public static final String MISSING_PAGES_FOLDER = "pages_not_installed/"; //$NON-NLS-1$

	public static final String ELEMENT_LINK_CLASS_elementLink = "elementLink"; //$NON-NLS-1$

	public static final String ELEMENT_LINK_CLASS_elementLinkWithType = "elementLinkWithType"; //$NON-NLS-1$

	public static final String ELEMENT_LINK_CLASS_elementLinkWithUserText = "elementLinkWithUserText"; //$NON-NLS-1$

	public static final String RESOURCE_FOLDER = MethodLibraryPersister.RESOURCE_FOLDER;

	public static final Pattern p_html_file_name = Pattern
			.compile("(.*),(.*)\\.html"); //$NON-NLS-1$

	// this one does not work when containing non-english characters, use the
	// more general one
	// public static final Pattern p_link_ref =
	// Pattern.compile("<a\\s+?([^>]*)>(.*?)</a>", Pattern.CASE_INSENSITIVE |
	// Pattern.DOTALL); //$NON-NLS-1$
	public static final Pattern p_link_ref = Pattern
			.compile(
					"<a\\s+?(.*?)>(.*?)</a>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_area_ref = Pattern
			.compile(
					"<area.*?(href\\s*=\\s*\"(.*?)\")", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_link_ref_gen = Pattern
			.compile(
					"<(a|area)\\s+?([^>]*)>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_link_type_picker = Pattern
			.compile(
					" class\\s*?=\\s*?(.*?)\\s+", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_link_guid_picker = Pattern
			.compile(
					" guid\\s*?=\\s*?(.*?)\\s+", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_link_href_picker = Pattern
			.compile(
					" href\\s*?=\\s*?\"(.*?)\"\\s+", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_tag_ref = Pattern
			.compile(
					"<([^>!]*)(\\n|\\r)([^>]*)>", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_image_ref = Pattern
			.compile(
					"(<(img|iframe).*?src\\s*=\\s*\")(.*?)(\")", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_url_decoder = Pattern
			.compile(
					"(<[^>]*?(src|href)\\s*=\\s*\")(.*?)(\"[^>]*?>)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_tag_attributes = Pattern
			.compile(
					"(.*?)\\s*=\\s*(\"?)(.*?)\\2", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static final Pattern p_css_ref = Pattern.compile(
			" url\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	// define constants for diagram type.
	// This will be used when linking diagrams into published contents
	// Activity diagram
	public static final String DIAGRAM_TYPE_WORKFLOW = "Activity"; //$NON-NLS-1$

	// activity detail diagram
	public static final String DIAGRAM_TYPE_ACTIVITY_DETAIL = "ActivityDetail"; //$NON-NLS-1$

	// WPDependency diagram
	public static final String DIAGRAM_TYPE_WP_DEPENDENCY = "WPDependency"; //$NON-NLS-1$

	public ResourceHelper() {
	}

	/**
	 * the relative path of the resource folder of the plugin in the library,
	 * relative to the library root
	 * 
	 * @param element
	 * @return String
	 */
	public static String getPluginResourcePath(MethodElement element) {
		MethodPlugin plugin = UmaUtil.getMethodPlugin(element);

		String path;
		if (plugin == null) {
			path = MethodLibraryPersister.getResourcePath(element);
		} else {
			path = MethodLibraryPersister.getResourcePath(plugin);
		}

		return fixPath(path);
	}

	/**
	 * get the relative path of the resource folder of the element in the
	 * library, relative to the library root
	 * 
	 * @param element
	 * @return
	 */
	public static String getElementResourcePath(MethodElement element) {
		String path = MethodLibraryPersister.getResourcePath(element);
		return fixPath(path);
	}

	/**
	 * get the relative path of the folder of the element in the library,
	 * relative to the library root
	 * 
	 * @param element
	 * @return
	 */
	public static String getElementPath(MethodElement element) {
		try {
			String path = MethodLibraryPersister.getElementPath(element);
			if (path == null || path.equals("")) //$NON-NLS-1$
			{
				System.out
						.println("Warning! No Path for Element [" + element.getType().getName() + ": " + element.getName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				return ""; //$NON-NLS-1$
			}
			return fixPath(path);
		} catch (RuntimeException e) {
			e.printStackTrace();
		}

		return ""; //$NON-NLS-1$
	}

	public static String fixPath(String path) {
		if (path == null || path.equals("")) //$NON-NLS-1$
		{
			return ""; //$NON-NLS-1$
		} else if (!path.endsWith(File.separator)) {
			return path + File.separator;
		} else {
			return path;
		}
	}

	/**
	 * get the path to the library root. For example,
	 * "RUP\guidance\concept\c1.xml", the back path is "./../../../"
	 * 
	 * @param path
	 * @return
	 */
	public static String getBackPath(MethodElement element) {
		// RATLC00384963 - Linux: Browsing and preview shows only plain text.
		// There are no images/sections
		// element path should check File.separatorChar instead of "\"
		String backPath = ""; //$NON-NLS-1$
		String path = getElementPath(element);
		if (path != null && path.length() > 0) {
			backPath = path.replace(File.separatorChar, '/').replaceAll(
					".*?/", "\\.\\./"); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return "./" + backPath; //$NON-NLS-1$
	}

	/**
	 * get the file name with the given extension
	 */
	public static String getFileName(MethodElement element, String ext) {
		return getFileName(element, null, null, ext);
	}

	/**
	 * get the file name with the given extension
	 */
	public static String getFileName(MethodElement element, String namePrefix,
			String nameSuffix, String ext) {
		StringBuffer buffer = new StringBuffer();
		if (namePrefix != null) {
			buffer.append(namePrefix);
		}

		// RATLC00386297 - Browsing: Browsing CP with special chars failed with
		// blank pages and Script Error
		// need to make a valid file name
		buffer.append(StrUtil.makeValidFileName(element.getName()));
		if (nameSuffix != null) {
			buffer.append(nameSuffix);
		}
		// if (element.getGuid().startsWith("{"))
		// {
		// String newGuid = element.getGuid().replaceFirst( OPENBRACE_STRING,
		// BRACE_REPLACEMENT );
		// buffer.append(",").append(newGuid.substring(0, newGuid.length()-1));
		// //$NON-NLS-1$
		// }
		// else
		buffer.append("," + element.getGuid()); //$NON-NLS-1$

		if (ext != null) {
			if (ext.indexOf(".") < 0) //$NON-NLS-1$
			{
				ext = "." + ext; //$NON-NLS-1$
			}
			buffer.append(ext);
		}

		return buffer.toString();
	}

	public static String getGuidFromFileName(String fileName) {
		if (fileName == null) {
			return null;
		}

		String guid = null;
		Matcher m = p_html_file_name.matcher(fileName);
		if (m.find()) {
			guid = m.group(2);
		}
		else {
			return null;
		}
		
		// turn off error reporting and see if guid exists in library
		boolean oldReport_Error = MultiFileResourceSetImpl.REPORT_ERROR;
		MultiFileResourceSetImpl.REPORT_ERROR = false;
		MethodElement e = LibraryService.getInstance().getCurrentLibraryManager()
				.getMethodElement(guid);
		MultiFileResourceSetImpl.REPORT_ERROR = oldReport_Error;
		if (e == null)
			return null;

		// if (null != guid && guid.startsWith(BRACE_REPLACEMENT))
		// {
		// StringBuffer guidBuffer = new StringBuffer(guid.replaceFirst(
		// BRACE_REPLACEMENT, OPENBRACE_STRING ));
		// guidBuffer.append("}");
		// return guidBuffer.toString();
		// }
		// else
		return guid;
	}

	public static MethodElement getElementFromFileName(String fileName) {
		String guid = getGuidFromFileName(fileName);
		if (guid != null) {
			ILibraryManager manager = LibraryService.getInstance()
					.getCurrentLibraryManager();
			if (manager != null) {
				return manager.getMethodElement(guid);
			}
		}

		return null;
	}

	/**
	 * get the element url relative to the refElement
	 * 
	 * @param element
	 * @param refElement
	 * @param fileExt
	 * @param old_url
	 *            the old url
	 * @return
	 */
	public static String getUrl(MethodElement element,
			MethodElement refElement, String fileExt, String old_url) {
		String url = getUrl(element, refElement, fileExt);

		// if the old url has bookmark in it, keep it
		if (old_url != null) {
			int indx = old_url.indexOf(URL_BOOKMARK_INDICATOR);
			if (indx < 0) {
				// keep url query string as well
				indx = old_url.indexOf(URL_PARAMETER_INDICATOR);
			}

			if (indx >= 0) {
				url = url + old_url.substring(indx);
			}
		}

		return url;
	}

	/**
	 * get the element url relative to the refElement
	 * 
	 * @param element
	 * @param refElement
	 * @param fileExt
	 * @return
	 */
	public static String getUrl(MethodElement element,
			MethodElement refElement, String fileExt) {
		if (element == null) {
			return ""; //$NON-NLS-1$
		}

		if (refElement == null) {
			return getElementPath(element).replace(File.separatorChar, '/')
					+ getFileName(element, fileExt);
		} else {
			return (getBackPath(refElement) + getElementPath(element)).replace(
					File.separatorChar, '/')
					+ getFileName(element, fileExt);
		}
	}

	public static String getLinkText(MethodElement e, String linkType) {
		String linkedText = null;
		if ((linkType != null)
				&& !ELEMENT_LINK_CLASS_elementLinkWithUserText.equals(linkType)) {
			if (ELEMENT_LINK_CLASS_elementLinkWithType.equals(linkType)) {
				linkedText = getElementLinkText(e, true);
			} else if (ELEMENT_LINK_CLASS_elementLink.equals(linkType)) {
				linkedText = getElementLinkText(e, false);
			}
		}

		return linkedText;
	}

	public static String getElementLinkText(MethodElement element,
			boolean withType) {
		String text = TngUtil.getPresentationName(element);

		if (withType) {
			return getElementTypeText(element) + ": " + text; //$NON-NLS-1$
		}

		return text;
	}

	public static String getElementTypeText(MethodElement element) {
		if (element == null) {
			return "";
		}

		return TngUtil.getTypeText(element);
	}

	/**
	 * auto generated element link <a class="elementLink" href="element_url"
	 * guid="element_guid">element_type: element_presentation_name</a>
	 * 
	 * @param element
	 *            MethodElement
	 * @param withType
	 *            boolean
	 * @param url
	 *            String
	 */
	public static String getElementLink(MethodElement element,
			boolean withType, String url) {
		String text = getElementLinkText(element, withType);
		String linkClass = ELEMENT_LINK_CLASS_elementLink;
		if (withType)
			linkClass = ELEMENT_LINK_CLASS_elementLinkWithType;
		return getElementLink(element, text, url, linkClass);
	}

	/**
	 * element link with user specified link text
	 * 
	 * @param element
	 * @param text
	 * @param url
	 * @return
	 */
	public static String getElementLink(MethodElement element, String text,
			String url) {
		return getElementLink(element, text, url,
				ELEMENT_LINK_CLASS_elementLinkWithUserText);
	}

	/**
	 * element link with user specified link text
	 * 
	 * @param element
	 * @param text
	 * @param url
	 * @return
	 */
	public static String getElementLink(MethodElement element, String text,
			String url, String linkClass) {
		return "<a class=\"" + linkClass + "\" " + getUrlText(url) + " guid=\"" + element.getGuid() + "\" >" + text + "</a>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
	}

	/**
	 * the passed in url might contain the href attribute with it, or even other
	 * attributes, so check if this is a pure url or not. return the url href
	 * term string
	 * 
	 * @param url
	 * @return
	 */
	private static String getUrlText(String url) {
		if (url == null) {
			url = ""; //$NON-NLS-1$
		} else {
			url = url.trim();
		}

		// starts with href, or contains an href term in it
		if (url.toLowerCase().startsWith("href") || url.toLowerCase().indexOf(" href") > 0) //$NON-NLS-1$ //$NON-NLS-2$
		{
			return url;
		}

		return "href=\"" + url + "\""; //$NON-NLS-1$ //$NON-NLS-2$
	}

	/**
	 * validate the content by checking element links and images
	 * 
	 * @param content
	 * @return
	 */
	public static String validateContent(MethodElement element, String source) {
		return validateContent(element, source, new DefaultContentValidator(),
				null);
	}

	public static boolean isElementLink(String linkType) {
		if (linkType == null || linkType.length() == 0) {
			return false;
		}

		return linkType.equals(ELEMENT_LINK_CLASS_elementLink)
				|| linkType.equals(ELEMENT_LINK_CLASS_elementLinkWithType)
				|| linkType.equals(ELEMENT_LINK_CLASS_elementLinkWithUserText);
	}

	/**
	 * validate the content by checking element links and images, for the
	 * specified element owner
	 * 
	 * @param element
	 *            MethodElement, the element that owns the content
	 * @param content
	 * @param config
	 *            MethodConfiguration the configuration to which the content is
	 *            validated
	 * @param linkedElements
	 *            List a passed in list to collect the linked elements in this
	 *            content, if null, no linked elements are collected.
	 * @return String the validated content
	 */
	public static String validateContent(MethodElement element, String source,
			IContentValidator validator, MethodConfiguration config) {
		try {
			// first validate the tags, remove any CF/LF from the tag text
			source = validateTag(source);

			StringBuffer sb = new StringBuffer();
			Matcher m = p_link_ref.matcher(source);

			while (m.find()) {
				String text = m.group();

				// RATLC00381344: Problems parsing <a href> tags
				// need to remove all LF, CR within the <a ..> tag
				String urltext = m.group(1);
				String linkedText = m.group(2);
				LinkInfo info = validator.validateLink(element, urltext,
						linkedText, config);
				if (info != null) {
					text = info.getHtml(validator.showBrokenLinks()).toString();
					MethodElement e = info.getLinkedElement();
					if (e != null) {
						validator.addReferencedElement(element, e);
					}
				}

				String replacement = text.replaceAll("file:///", ""); //$NON-NLS-1$ //$NON-NLS-2$
				replacement = replacement.replaceAll("file://", ""); //$NON-NLS-1$ //$NON-NLS-2$
				m.appendReplacement(sb, replacement);
			}

			m.appendTail(sb);

			if (element == null) {
				return sb.toString();
			}

			// also fix the area map
			source = sb.toString();
			m = p_area_ref.matcher(source);
			sb.setLength(0);

			ILibraryManager manager = LibraryService.getInstance()
					.getCurrentLibraryManager();
			while (m.find()) {
				// String text = m.group();
				String href = m.group(2);
				String guid = null;
				if ( !isJavascriptUrl(href) ) {
					guid = getGuidFromFileName(href);
				}
				
				if (guid != null) {
					MethodElement e = manager != null ? manager
							.getMethodElement(guid) : null;
					if (e != null) {
						// should realize the element, and get the realized one
						MethodElement e1 = ConfigurationHelper
								.getCalculatedElement(e, config);
						if (e1 != null) {
							e = e1;
						}

						validator.addReferencedElement(element, e);

						if (ConfigurationHelper.inConfig(e, config)
								&& !validator.isDiscarded(element, null, e)) {
							href = getUrl(e, element, FILE_EXT_HTML, href);
						} else if ( validator.showBrokenLinks() ){
							href = getDiscardedElementURL(element, e, guid,
									((DefaultContentValidator) validator)
											.getPublishDir());
						} else {
							href = "";
						}
					}
				} else {
					href = URLDecoder.decode(href, "UTF-8"); //$NON-NLS-1$
				}

				String replacement;			
				if ( href == null || href.length() == 0 ) {
					replacement = source.substring(m.start(), m.start(1))
					+ source.substring(m.end(1), m.end());
				} else {
					replacement = source.substring(m.start(), m.start(2))
						+ href + source.substring(m.end(2), m.end());
				}
				m.appendReplacement(sb, replacement);
			}

			m.appendTail(sb);

			// // RATLC00384332 - GVT: Shape icon broken in preview and browsing
			// // need to decode the image path unless we can disable the url
			// encoding in xslt
			// source = sb.toString();
			// sb.setLength(0);
			// m = ResourceHelper.p_image_ref.matcher(source);
			// while ( m.find() )
			// {
			// String url = m.group(3);
			// url = URLDecoder.decode(url, "UTF-8");
			// m.appendReplacement(sb, m.group(1) + url + m.group(4));
			// }
			// m.appendTail(sb);
			// return sb.toString();

			// decode the urls
			return decodeUrlsInContent(sb.toString());

		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return source;
	}

	/**
	 * decode the urls in the content. Content from xslt output are encoded. We
	 * don't want this to be encoded since the browser may decode it in a wrong
	 * encoding.
	 * 
	 * @param content
	 * @return String
	 */
	public static String decodeUrlsInContent(String content) {
		try {
			// RATLC00384332 - GVT: Shape icon broken in preview and browsing
			// need to decode the image path unless we can disable the url
			// encoding in xslt
			StringBuffer sb = new StringBuffer();
			Matcher m = ResourceHelper.p_url_decoder.matcher(content);
			while (m.find()) {
				String url = m.group(3);
				url = URLDecoder.decode(url, "UTF-8"); //$NON-NLS-1$
				m.appendReplacement(sb, m.group(1) + url + m.group(4));
			}
			m.appendTail(sb);

			return sb.toString();

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		return content;
	}

	/**
	 * fix the element url in the urltext. the text is the part from the
	 * p_link_ref pattern match
	 * 
	 * @param urltext
	 * @param config
	 *            MethodConfiguration the configuration to which the content is
	 *            validated
	 * @return String the fixed text
	 */
	public static String fixElementUrl(MethodElement element, String urltext,
			MethodConfiguration config) {
		Matcher m = p_link_href_picker.matcher(" " + urltext + " "); //$NON-NLS-1$ //$NON-NLS-2$
		StringBuffer sb = new StringBuffer();
		if (m.find()) {
			String url = m.group(1);
			String newurl = null;
			if (element != null) {
				String guid = getGuidFromUrl(urltext);
				if (guid != null) {
					ILibraryManager manager = LibraryService.getInstance()
							.getCurrentLibraryManager();
					MethodElement e = manager != null ? manager
							.getMethodElement(guid) : null;
					if (e != null) {
						if (config != null) {
							e = ConfigurationHelper.getCalculatedElement(e,
									config);
						}
						newurl = getUrl(e, element, FILE_EXT_HTML, url);
					}

					// if the element is null, we should remove the url link and
					// log an error
					// TODO
				}
			}

			if (newurl != null && !newurl.equals(url)) {
				String replacement = " href=\"" + newurl + "\" "; //$NON-NLS-1$ //$NON-NLS-2$
				m.appendReplacement(sb, replacement);
				m.appendTail(sb);
				return sb.toString();

			}
		}

		return urltext;
	}

	public static String getElementLinkType(String source) {
		Matcher m = p_link_type_picker.matcher(" " + source + " "); //$NON-NLS-1$ //$NON-NLS-2$
		if (m.find()) {
			return m.group(1).trim().replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
		}

		return null;
	}

	public static String getGuidFromUrl(String source) {
		Matcher m = p_link_guid_picker.matcher(" " + source + " "); //$NON-NLS-1$ //$NON-NLS-2$
		if (m.find()) {
			return m.group(1).trim().replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
		}

		m = p_link_href_picker.matcher(" " + source + " "); //$NON-NLS-1$ //$NON-NLS-2$
		if (m.find()) {
			String href = m.group(1).trim().replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
			return getGuidFromFileName(href);
		}

		return null;
	}

	/**
	 * handle a bug in IE. anchor tag can't have CR LF, like <A \r\n
	 * href="www.yahoo.com" \r\n> scan the string source, find all anchor tags
	 * and replace with space.
	 * 
	 * @param source
	 * @return String the fixed source
	 */
	public static String validateTag(String source) {
		// Pattern p_anchor_ref = Pattern.compile("(*+?)=(*.?)",
		// Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

		StringBuffer sb = new StringBuffer();
		Matcher m = p_tag_ref.matcher(source);
		// StringBuffer tempSb = new StringBuffer();

		while (m.find()) {
			String text = m.group();
			String replacement = fixTagAttribute(text);
			m.appendReplacement(sb, replacement);
		}

		m.appendTail(sb);

		return sb.toString();
	}

	public static String fixTagAttribute(String source) {
		// simply replace all CR LF with ' ', fine tune later
		return source
				.replaceAll("\\\r\\\n", " ").replaceAll("\\\r", " ").replaceAll("\\\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
	}

	/**
	 * get the resource file url relative to the library root
	 * 
	 * @param e
	 *            MethodElement the element that references this resource
	 * @param file
	 *            File the resource's physical path
	 * @return String the url to this resource from the element
	 */
	public static String getResourceFileUrl(MethodElement e, File file) {
		String fileUrl = getFileUrl(file);
		// String elementPath =
		// ResourceHelper.getElementResourcePath(e).replace(File.separatorChar,
		// '/');

		fileUrl = ResourceHelper.getBackPath(e) + fileUrl;

		return fileUrl;

	}

	/**
	 * return the file's relative path to the plugin root
	 * 
	 * @param f
	 * @return
	 */
	public static String getFileUrl(File f) {
		String path = getFileRelPath(f);
		path = path.replace(File.separatorChar, '/');
		return path;
	}

	/**
	 * get the URI relative to the specified base
	 * 
	 * @param uri
	 * @return java.net.URI
	 */
	public static URI getRelativeURI(URI uri, String rootPath) {
		return getRelativeURI(uri, new File(rootPath).toURI());
	}

	/**
	 * get the URI relative to the specified base
	 * 
	 * @param uri
	 * @return java.net.URI
	 */
	public static URI getRelativeURI(URI uri, URI relativeTo) {
		URI relUri = relativeTo.relativize(uri);
		return relUri;
	}

	/**
	 * resolve the relative path to the library root
	 * 
	 * @param f
	 * @return String the raltive path
	 */
	public static String getFileRelPath(File f) {
		File libraryRootPath = new File(LibraryService.getInstance()
				.getCurrentMethodLibraryPath());
		String root = libraryRootPath.getAbsolutePath();
		String path = f.getAbsolutePath();
		if (path.startsWith(root)) {
			path = path.substring(root.length());
		}

		if (path.startsWith(File.separator)) {
			path = path.substring(1);
		}

		return path;
	}

	/**
	 * get the URI relative to the library root
	 * 
	 * @param f
	 * @return
	 */
	public static java.net.URI getFileRelativeURI(File f) {
		File libraryRootPath = new File(LibraryService.getInstance()
				.getCurrentMethodLibraryPath());
		java.net.URI libURI = libraryRootPath.toURI();
		java.net.URI fileURI = f.toURI();
		java.net.URI relUri = libURI.relativize(fileURI);

		return relUri;
	}

	/**
	 * copy the file to the specified destination
	 * 
	 * @param source
	 * @param dest
	 * @return
	 */
	public static boolean copyFile(File source, File dest) {
		if ((source == null) || !source.exists()) {
			return false;
		}

		boolean needCopy = true;
		if (dest.exists()) {
			needCopy = (dest.lastModified() != source.lastModified())
					|| (dest.length() != source.length());
		}

		if (needCopy) {
			File parent = dest.getParentFile();
			if (!parent.exists()) {
				parent.mkdirs();
			}

			try {
				if (!dest.exists()) {
					dest.createNewFile();
				}

				FileUtil.copyFile(source, dest);
			} catch (Exception ex) {
				ex.printStackTrace();

				return false;
			}
		}

		return true;
	}

	/**
	 * Returns file URL for an attachment
	 * 
	 * @param attachment
	 *            the file to attach
	 * @param element
	 *            the MethodElement referencing the file
	 * @param copyFile
	 *            if true, will copy the file (if it isn't already in the plugin
	 *            path)
	 * @return URL of the form ./../&lt;roles&gt;/resources/&lt;filename of
	 *         attachment&gt;
	 * @throws IOException
	 */
	public static String getURLForAttachment(File attachment,
			MethodElement element, boolean copyFile) throws IOException {
		String libDir = LibraryService.getInstance()
				.getCurrentMethodLibraryPath();
		String pluginDir = FileUtil.appendSeparator(new File(UmaUtil.getMethodPlugin(element)
				.eResource().getURI().toFileString()).getParent());
		File formatFile = null;
		if (attachment.getAbsolutePath().startsWith(pluginDir)) {
			formatFile = attachment;
		} else {
			String resourceLoc = libDir + File.separator
					+ MethodLibraryPersister.getResourcePath(element);
			// File resourceDir = new File(resourceLoc);
			formatFile = new File(resourceLoc + File.separator
					+ attachment.getName());
			if (copyFile)
				FileUtil.copyfile(attachment, formatFile);
		}

		return ResourceHelper.getRelativePathToFileFromElement(element,
				formatFile);

	}

	public static String getRelativePathToFileFromElement(
			MethodElement element, File attachment) {
		String libDir = LibraryService.getInstance()
				.getCurrentMethodLibraryPath();
		// String pluginDir = new
		// File(UmaUtil.getMethodPlugin(element).eResource().getURI().toFileString()).getParent();
		String elementLoc = libDir + File.separator + getElementPath(element);
		return FileUtil.getRelativePath(attachment, new File(elementLoc));
	}

	/**
	 * get the file path name of the type of diagram for the specified element
	 * 
	 * @param e
	 * @param diagramType
	 *            String diagram type defined in one of the diagram type
	 *            constants
	 * @return String the file path relative to the library root
	 */
	public static String getDiagramFilePathName(MethodElement e,
			String diagramType) {
		// RATLC00384528 (Submit): Instances of the same activity/CP share the
		// same diagram in a Delivery Process
		// need to make the name unique
		return getElementResourcePath(e)
				+ StrUtil.makeValidFileName(e.getName())
				+ "_" + e.getGuid() + "_" + diagramType + FILE_EXT_JPEG; //$NON-NLS-1$ //$NON-NLS-2$
	}

	public static final String FILE_PREFIX_2 = "file://"; //$NON-NLS-1$

	public static final String FILE_PREFIX_3 = "file:///"; //$NON-NLS-1$

	/**
	 * get the relative path of the file based on the url
	 * 
	 * @param url
	 *            String the url
	 * @param contentPath
	 *            String the path of the content that contains this url
	 * @return String the file path relative to the content publishing root
	 */
	public static String getFilePathFromUrl(String url, String contentPath) {
		// System.out.println("Processing image url [" + url + "]");
		if (isExternalLink(url)
				|| url.startsWith("mailto:") || url.startsWith(URL_BOOKMARK_INDICATOR)) //$NON-NLS-1$ 
		{
			return null;
		}

		int index = url.indexOf(FILE_PREFIX_3);
		if (index == 0) {
			url = url.substring(FILE_PREFIX_3.length());
		}

		index = url.indexOf(FILE_PREFIX_2);
		if (index == 0) {
			url = url.substring(FILE_PREFIX_2.length());
		}

		File f = new File(contentPath);
		int start = 0;
		index = 0;
		while ((f != null) && (index = url.indexOf("../", start)) >= 0) //$NON-NLS-1$
		{
			f = f.getParentFile();
			start = index + 3;
		}

		String rootFolder = ""; //$NON-NLS-1$
		if (f != null) {
			rootFolder = f.getPath();
		}

		if (rootFolder.length() > 0 && !rootFolder.endsWith(File.separator)) {
			rootFolder += File.separator;
		}

		// the file path is
		String path = rootFolder
				+ url.substring(start).replace('/', File.separatorChar);

		return XMLUtil.unescape(NetUtil.decodedFileUrl(path));
	}

	/**
	 * for the given content with url based on the oldContentPath, resolve to
	 * the path relative to the root of the contentPath. If backpath is
	 * specified, add the backPath. return the updated content. A typical
	 * scenario is the content is from a contributing element, which has urls
	 * relative to the contributing element's contentPath. This method will fix
	 * the url to based on the base element's contentPath.
	 * 
	 * So call fixContentUrlPath(contributor_text, contributor_elementPath,
	 * baseElement_backPath)
	 * 
	 * @param content
	 * @param contentPath
	 * @param oldContentPath
	 * @return String the updated content with fixed path.
	 */
	public static String fixContentUrlPath(String content, String contentPath,
			String backPath) {
		StringBuffer sb = new StringBuffer();
		try {
			// process images
			Matcher m = ResourceHelper.p_image_ref.matcher(content);
			while (m.find()) {
				String url = m.group(3);
				url = resolveUrl(url, contentPath, backPath);
				if (url != null) {
					m.appendReplacement(sb, m.group(1) + url + m.group(4));
				}
			}
			m.appendTail(sb);

			content = sb.toString();
			sb = new StringBuffer();

			// process attachments
			m = ResourceHelper.p_link_ref_gen.matcher(content);
			while (m.find()) {
				StringBuffer sbLink = new StringBuffer();
				// String tag = m.group(1);
				String urltext = " " + m.group(2) + " "; //$NON-NLS-1$ //$NON-NLS-2$
				if (ResourceHelper.getGuidFromUrl(urltext) == null) {
					Matcher m2 = ResourceHelper.p_link_href_picker
							.matcher(urltext);
					if (m2.find()) {
						String url = m2.group(1).trim().replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
						url = resolveUrl(url, contentPath, backPath);
						if (url != null) {
							m2.appendReplacement(sbLink, urltext.substring(m2
									.start(), m2.start(1))
									+ url
									+ urltext.substring(m2.end(1), m2.end()));
							m2.appendTail(sbLink);
							m.appendReplacement(sb, content.substring(
									m.start(), m.start(2))
									+ sbLink.toString()
									+ content.substring(m.end(2), m.end()));
						}
					}
				}

			}
			m.appendTail(sb);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return sb.toString();
	}

	public static String resolveUrl(String url, String contentPath,
			String backPath) {
		url = getFilePathFromUrl(url, contentPath);
		if (url != null) {
			if (backPath != null) {
				url = backPath + url;
			}

			return url.replace(File.separatorChar, '/');
		}

		return null;
	}

	private static final String EXTERNAL_URL_START_WITH = LayoutResources
			.getString("externalUrl_startWith"); //$NON-NLS-1$

	public static final Pattern p_external_url_startWith = Pattern
			.compile(
					"(" + EXTERNAL_URL_START_WITH + ")", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); //$NON-NLS-1$

	public static boolean isExternalLink(String url) {
		// return url.startsWith("http://") || url.startsWith("www.") ||
		// url.startsWith("ftp://") ; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		Matcher m = p_external_url_startWith.matcher(url);
		if (m.find()) {
			return (m.start(1) == 0);
		}

		return false;
	}

	public static boolean isJavascriptUrl(String url) {
		if (url == null ) {
			return false;
		}
		return url.startsWith(URL_STR_MAILTO)
				|| url.toLowerCase().startsWith(URL_STR_JAVASCRIPT);
	}
	
	/**
	 * find the resource file based on the url, copy the file to the destination
	 * folder. if the file is an html attachment, scan the file recursively and
	 * copy contained images, resources.
	 * 
	 * @param owner
	 *            MethodElement, the owner element
	 * @param resourceFile
	 *            File, the resource file
	 * @param url
	 *            String the url to be processed
	 * @param contentPath
	 *            String the content path of the url owner, relative to the
	 *            source root path
	 * @param sourceRootPath
	 *            File the source root path, usually it's the library root path
	 * @param targetRootPath
	 *            File the target root path, usually it's the publishing root
	 *            path
	 * @param processedItems
	 *            List, a list to hold all processed urls, to avoid duplicate
	 *            processing
	 * @param validator
	 *            IContentValidator
	 * @return String the fixed url, not used.
	 */
	public static String processResourceUrl(MethodElement owner,
			File resourceFile, String url, String contentPath,
			File sourceRootPath, File targetRootPath, List processedItems,
			IContentValidator validator) {
		if (url == null || isExternalLink(url) || isJavascriptUrl(url)
				|| url.startsWith(URL_BOOKMARK_INDICATOR)) {
			return url;
		}

		// if the url contains the guid, it might be some kind of element link,
		// for example, activity can have model than one element link
		String guid = ""; //$NON-NLS-1$
		if (owner != null)
			guid = owner.getGuid();
		if (url.indexOf(guid) >= 0) {
			return url;
		}

		// RATLC00384586 - Publishing report lists missing resource files that
		// are present
		// bookmarks should be stripped out when checking resource
		int index = url.indexOf(URL_BOOKMARK_INDICATOR);
		if (index < 0) {
			index = url.indexOf(URL_PARAMETER_INDICATOR);
		}

		String url_tail = "";
		if (index > 0) {
			url_tail = url.substring(index);
			url = url.substring(0, index);
		}

		String imageFile = ResourceHelper.getFilePathFromUrl(url, contentPath);
		if (imageFile == null) {
			if (validator != null) {
				validator.logMissingResource(owner, resourceFile, url);
			}
			return null;
		}

		// index.htm is the default index file, always ignore it
		if (imageFile.equalsIgnoreCase("index.htm") || imageFile.equalsIgnoreCase("index.html")) //$NON-NLS-1$ //$NON-NLS-2$
		{
			return url + url_tail;
		}

		String newUrl = url;

		File source;
		File dest;
		// check if this is a real file, if not, it is a reference to a base
		// plugin
		source = new File(sourceRootPath, imageFile);

		// if the file is not in the library, check the plugin layout folder
		if (!source.exists()) {
			source = new File(LibraryPlugin.getDefault().getLayoutPath(),
					imageFile);
		}

		dest = new File(targetRootPath, imageFile);

		if (source.exists()) {
			ResourceHelper.copyFile(source, dest);

			// if the file is an html attachment, need to scan the file and copy
			// the referenced file in it, recursively
			if (imageFile.endsWith(ResourceHelper.FILE_EXT_HTM)
					|| imageFile.endsWith(ResourceHelper.FILE_EXT_HTML)) {
				File f = new File(imageFile);
				try {
					resolveResources(owner, f, FileUtil.readFile(source,
							FileUtil.ENCODING_UTF_8).toString(), f.getParent(),
							sourceRootPath, targetRootPath, processedItems,
							validator);
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
		// if the destination file is there, might be generated dynamically, so
		// don't log it
		else if (!dest.exists() && isValidFileUrl(url) && (validator != null)) {
			validator.logMissingResource(owner, resourceFile, url);
		}

		return newUrl + url_tail;

	}

	private static boolean isValidFileUrl(String url) {
		// just a temp solution here to get rid of the wrong reporting of mssing
		// resource
		if (url == null || url.equals("' + diagram_img + '")) { //$NON-NLS-1$
			return false;
		}

		return true;
	}

	/**
	 * resolve the images in the text. copy the image to destination if needed
	 * This is used for copying resources from a library to another destination
	 * 
	 * @param owner
	 *            MethodElement, the owner element
	 * @param resourceFile
	 *            File, the resource file
	 * @param content
	 *            String the content string to be processed
	 * @param contentPath
	 *            String the content path of the source text, relative to the
	 *            source root path
	 * @param sourceRootPath
	 *            File the source root path, usually it's the library root path
	 * @param targetRootPath
	 *            File the target root path, usually it's the publishing root
	 *            path
	 * @param processedItems
	 *            List, a list to hold all processed urls, to avoid duplicate
	 *            processing
	 * @param validator
	 *            IContentValidator
	 */
	public static void resolveResources(MethodElement owner, File resourceFile,
			String content, String contentPath, File sourceRootPath,
			File targetRootPath, List processedItems,
			IContentValidator validator) {
		try {
			// process images
			Matcher m = ResourceHelper.p_image_ref.matcher(content);
			while (m.find()) {
				String url = m.group(3);
				if (!processedItems.contains(url)) {
					// note: add the url into the processedItems list first,
					// otherwise may get deadlock if the url content has
					// reference back to the owner
					processedItems.add(url);
					ResourceHelper.processResourceUrl(owner, resourceFile, url,
							contentPath, sourceRootPath, targetRootPath,
							processedItems, validator);
				}
			}

			m = ResourceHelper.p_css_ref.matcher(content);
			while (m.find()) {
				String cssURL = m.group(1);

				// it's hard to tell if this is a right match or not
				// since we can't tell if the matched one is actually from a css
				// or just a html text
				// for example, text can have something like url(xxx) which is
				// matched here
				// we don't want to report missing resource for this
				// so don't set the validator
				// RATLC00386230 - Publishing warnings: inappropriate warnings
				// coming from required copyrite.htm

				if (!processedItems.contains(cssURL)) {
					// note: add the url into the processedItems list first,
					// otherwise may get deadlock if the url content has
					// reference back to the owner
					processedItems.add(cssURL);
					ResourceHelper
							.processResourceUrl(owner, resourceFile, cssURL,
									contentPath, sourceRootPath,
									targetRootPath, processedItems, null/* validator */);
				}
			}

			// process attachments
			m = ResourceHelper.p_link_ref_gen.matcher(content);
			while (m.find()) {
				// String tag = m.group(1);
				String urltext = m.group(2);
				if (ResourceHelper.getGuidFromUrl(urltext) == null) {
					Matcher m2 = ResourceHelper.p_link_href_picker
							.matcher(" " + urltext + " "); //$NON-NLS-1$ //$NON-NLS-2$
					if (m2.find()) {
						String url = m2.group(1).trim().replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
						if (!processedItems.contains(url)) {
							// note: add the url into the processedItems list
							// first, otherwise may get deadlock if the url
							// content has reference back to the owner
							processedItems.add(url);
							ResourceHelper.processResourceUrl(owner,
									resourceFile, url, contentPath,
									sourceRootPath, targetRootPath,
									processedItems, validator);
						}
					}
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * take the tag attributes string as input and returns the attribute
	 * name-value map
	 * 
	 * @param attributesStr
	 * @return Map the attribute name-value map
	 */
	public static Map getTagAttributes(String attributesStr) {

		// use LinkedHashMap to reserve the order of the attributes
		Map attributeMap = new LinkedHashMap(3);
		Matcher m2 = p_tag_attributes.matcher(attributesStr);
		while (m2.find()) {
			String attrName = m2.group(1).trim().toLowerCase();
			String attrValue = m2.group(3).trim();

			// RATLC00384297 - GVT: Cannot Preview/Browse Description Tab when
			// the CP contains chinese characters
			// generated html from xslt got the href urls encoded. we don't want
			// that
			// make sure decode the url using "UTF-8" encoding
			if (attrName.equals(TAG_ATTR_HREF)) {
				try {
					attrValue = URLDecoder.decode(attrValue, "UTF-8"); //$NON-NLS-1$
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
			}
			attributeMap.put(attrName, attrValue);
		}

		return attributeMap;
	}

	/**
	 * returns the attribute name-value map for an HTML link
	 * 
	 * @param link
	 * @return
	 */
	public static Map getAttributesFromLink(String link) {
		Matcher m = p_link_ref.matcher(link);
		if (m.find()) {
			String attributes = m.group(1);
			return getTagAttributes(attributes);
		}
		return null;
	}

	/**
	 * takes an attributeMap (ie, from #getTagAttributes) and returns the String
	 * representation
	 * 
	 * @param attributeMap
	 * @return
	 */
	public static String getAttributesAsString(Map attributeMap) {
		StringBuffer buf = new StringBuffer();
		for (Iterator iter = attributeMap.keySet().iterator(); iter.hasNext();) {
			String key = (String) iter.next();
			String value = (String) attributeMap.get(key);
			if (buf.length() > 0)
				buf.append(" "); //$NON-NLS-1$
			buf.append(key + "=\"" + value + "\""); //$NON-NLS-1$ //$NON-NLS-2$
		}
		return buf.toString();
	}

	/**
	 * Created to handle RATLC00385815: DVT - Bad link from Welcome page hump
	 * chart
	 * 
	 */
	private static String getDiscardedElementURL(MethodElement ownerElement,
			MethodElement linkElement, String guid, String pubDir) {
		StringBuffer discardedElementURL = new StringBuffer();

		discardedElementURL.append(MISSING_PAGES_FOLDER).append(
				"pages_not_installed").append(FILE_EXT_HTML);

		File outputFile = new File(pubDir, discardedElementURL.toString());
		File dir = outputFile.getParentFile();
		if (!dir.exists()) {
			dir.mkdirs();
		}

		// generate the html file
		XmlElement xml = new XmlElement("Element"); //$NON-NLS-1$

		xml
				.setAttribute("guid", guid).setAttribute("type", linkElement.getType().getName()) //$NON-NLS-1$
				.setAttribute("name", linkElement.getName()) //$NON-NLS-1$
				.setAttribute(
						"pluginName", LibraryUtil.getMethodPlugin(linkElement).getName()); //$NON-NLS-1$								
		String xslPath = LayoutResources.getXslUri("page_not_installed", null); //$NON-NLS-1$

		try {
			StringBuffer xml2 = new StringBuffer();
			xml2.append(XmlHelper.XML_HEADER).append(xml.toXml());

			OutputStreamWriter output = new OutputStreamWriter(
					new FileOutputStream(outputFile), "utf-8"); //$NON-NLS-1$
			Properties xslParams = LibraryPlugin.getDefault().getProperties(
					"/layout/xsl/resources.properties"); //$NON-NLS-1$

			XSLTProcessor
					.transform(xslPath, xml2.toString(), xslParams, output);
			output.flush();
			output.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return getBackPath(ownerElement).replace(File.separatorChar, '/')
				+ discardedElementURL.toString();
	}
	
	
	
	/**
	 * FOR USE WITH RICH TEXT EDITOR
	 * validate the content by checking element links and images, for the
	 * specified element owner
	 * 
	 * This is identical to the validateContent method, except it does not decode the results
	 * via URLDecoder.decode()
	 * 
	 * @param element
	 *            MethodElement, the element that owns the content
	 * @param content
	 * @param config
	 *            MethodConfiguration the configuration to which the content is
	 *            validated
	 * @param linkedElements
	 *            List a passed in list to collect the linked elements in this
	 *            content, if null, no linked elements are collected.
	 * @return String the validated content
	 */
	public static String validateRichTextContent(MethodElement element, String source, IContentValidator validator) {
 		try {
			MethodConfiguration config = null;
			
			// first validate the tags, remove any CF/LF from the tag text
			source = validateTag(source);

			StringBuffer sb = new StringBuffer();
			Matcher m = p_link_ref.matcher(source);

			while (m.find()) {
				String text = m.group();

				// RATLC00381344: Problems parsing <a href> tags
				// need to remove all LF, CR within the <a ..> tag
				String urltext = m.group(1);
				String linkedText = m.group(2);
				LinkInfo info = validator.validateLink(element, urltext,
						linkedText, config);
				if (info != null) {
					text = info.getHtml(validator.showBrokenLinks()).toString();
					MethodElement e = info.getLinkedElement();
					if (e != null) {
						validator.addReferencedElement(element, e);
					}
				}

				String replacement = text.replaceAll("file:///", ""); //$NON-NLS-1$ //$NON-NLS-2$
				replacement = replacement.replaceAll("file://", ""); //$NON-NLS-1$ //$NON-NLS-2$
				m.appendReplacement(sb, replacement);
			}

			m.appendTail(sb);

			if (element == null) {
				return sb.toString();
			}

			// also fix the area map
			source = sb.toString();
			m = p_area_ref.matcher(source);
			sb.setLength(0);

			ILibraryManager manager = LibraryService.getInstance()
					.getCurrentLibraryManager();
			while (m.find()) {
				// String text = m.group();
				String href = m.group(2);
				String guid = getGuidFromFileName(href);
				if (guid != null) {
					MethodElement e = manager != null ? manager
							.getMethodElement(guid) : null;
					if (e != null) {
						// should realize the element, and get the realized one
						MethodElement e1 = ConfigurationHelper
								.getCalculatedElement(e, config);
						if (e1 != null) {
							e = e1;
						}

						validator.addReferencedElement(element, e);

						if (ConfigurationHelper.inConfig(e, config)
								&& !validator.isDiscarded(element, null, e)) {
							href = getUrl(e, element, FILE_EXT_HTML, href);
						} else if ( validator.showBrokenLinks() ){
							href = getDiscardedElementURL(element, e, guid,
									((DefaultContentValidator) validator)
											.getPublishDir());
						} else {
							href = "";
						}
					}
				}
				
				String replacement;			
				if ( href == null || href.length() == 0 ) {
					replacement = source.substring(m.start(), m.start(1))
					+ source.substring(m.end(1), m.end());
				} else {
					replacement = source.substring(m.start(), m.start(2))
						+ href + source.substring(m.end(2), m.end());
				}
			
				m.appendReplacement(sb, replacement);
			}

			m.appendTail(sb);

			return sb.toString();

		} catch (Exception ex) {
			LibraryPlugin.getDefault().getLogger().logError(ex);
		}

		return source;
	}

}
