| //------------------------------------------------------------------------------ |
| // 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.publishing.services; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.PrintStream; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.epf.common.utils.Timer; |
| import org.eclipse.epf.library.layout.DefaultContentValidator; |
| import org.eclipse.epf.library.layout.LinkInfo; |
| import org.eclipse.epf.library.layout.util.XmlElement; |
| import org.eclipse.epf.library.util.LibraryUtil; |
| import org.eclipse.epf.library.util.ResourceHelper; |
| import org.eclipse.epf.publishing.PublishingPlugin; |
| import org.eclipse.epf.publishing.PublishingResources; |
| import org.eclipse.epf.publishing.util.http.HttpResponse; |
| import org.eclipse.epf.publishing.util.http.HttpUtil; |
| import org.eclipse.epf.uma.ContentCategory; |
| import org.eclipse.epf.uma.MethodConfiguration; |
| import org.eclipse.epf.uma.MethodElement; |
| import org.eclipse.osgi.util.NLS; |
| |
| import com.ibm.icu.util.Calendar; |
| |
| /** |
| * content validator used for publishing. This class will be responsible for validating the content to be published, |
| * fixing problems such as links in the content, |
| * and logging information about missing elements, missing resources, etc. |
| * |
| * @author Jinhua Xi |
| * @since 1.0 |
| * |
| */ |
| public class PublishingContentValidator extends DefaultContentValidator { |
| |
| private boolean debug = PublishingPlugin.getDefault().isDebugging(); |
| |
| private boolean showExtraInfoForDescriptors = false; |
| |
| class InvalidExternalLinkInfo { |
| public MethodElement owner; |
| |
| public String url; |
| |
| public String message; |
| |
| public InvalidExternalLinkInfo(MethodElement owner, String url, |
| String message) { |
| this.owner = owner; |
| this.url = url; |
| this.message = message; |
| } |
| } |
| |
| class MissingReference { |
| public MethodElement owner; |
| |
| public MethodElement refElement; |
| |
| public String guid; |
| |
| public String linkedText; |
| |
| public MissingReference(MethodElement owner, MethodElement refElement) { |
| this.owner = owner; |
| this.refElement = refElement; |
| } |
| |
| public MissingReference(MethodElement owner, String guid, |
| String linkedText) { |
| this.owner = owner; |
| this.guid = guid; |
| this.linkedText = linkedText; |
| } |
| } |
| |
| class MissingResource { |
| public MethodElement owner; |
| |
| public File resourceFile; |
| |
| public String url; |
| |
| public MissingResource(MethodElement owner, File resourceFile, |
| String url) { |
| this.owner = owner; |
| this.resourceFile = resourceFile; |
| this.url = url; |
| } |
| } |
| |
| static final String LOGS_FOLDER = "logs"; //$NON-NLS-1$ |
| |
| static final String ERROR_LOG_FILENAME = "error.log"; //$NON-NLS-1$ |
| |
| static final String WARNING_LOG_FILENAME = "warning.log"; //$NON-NLS-1$ |
| |
| static final String INFO_LOG_FILENAME = "info.log"; //$NON-NLS-1$ |
| |
| protected File logPath; |
| |
| protected boolean validateExternalLinks = false; |
| |
| protected List invalidExternalLinks = new ArrayList(); |
| |
| // cache the valided external links to avoid multiple checking |
| protected List validatedExternalLinks = new ArrayList(); |
| |
| protected List missingReferences = new ArrayList(); |
| |
| protected List missingResources = new ArrayList(); |
| |
| protected List discardedElements = new ArrayList(); |
| |
| protected List validCategories = new ArrayList(); |
| |
| protected long publishing_start = 0; |
| |
| protected long time_for_external_link_checking = 0; |
| |
| // collect the elements referenced by the published contents so we can |
| // publish them |
| // this will be the elements to be published |
| protected List referencedElements = new ArrayList(); |
| |
| // published elements |
| protected List publishedElements = new ArrayList(); |
| |
| // this is the default target element for the content validator |
| // set this before publishing the element and set to null after the |
| // publishign is done |
| protected MethodElement defaultTarget = null; |
| |
| /** |
| * consructor |
| * |
| * @param pubDir String |
| * @param validateExternalLinks boolean |
| */ |
| public PublishingContentValidator(String pubDir, |
| boolean validateExternalLinks) { |
| super(pubDir); |
| this.validateExternalLinks = validateExternalLinks; |
| |
| this.logPath = new File(pubDir, LOGS_FOLDER); |
| super.info = getStream(INFO_LOG_FILENAME); |
| super.warning = getStream(WARNING_LOG_FILENAME); |
| super.error = getStream(ERROR_LOG_FILENAME); |
| |
| // set the start time |
| publishing_start = Calendar.getInstance().getTimeInMillis(); |
| } |
| |
| /** |
| * dispose the object |
| */ |
| public void dispose() { |
| invalidExternalLinks.clear(); |
| validatedExternalLinks.clear(); |
| missingReferences.clear(); |
| missingResources.clear(); |
| discardedElements.clear(); |
| validCategories.clear(); |
| |
| referencedElements.clear(); |
| publishedElements.clear(); |
| |
| info.close(); |
| warning.close(); |
| error.close(); |
| |
| } |
| |
| protected PrintStream getStream(String fileName) { |
| try { |
| File f = new File(logPath, fileName); |
| File dir = f.getParentFile(); |
| dir.mkdirs(); |
| |
| if (!f.exists()) { |
| f.createNewFile(); |
| } |
| |
| return new PrintStream(new FileOutputStream(f), true); |
| } catch (Exception e) { |
| |
| } |
| |
| return null; |
| } |
| |
| /** |
| * validate the link attributes fro the element. |
| * |
| * @param owner MethodElement the owner element |
| * @param attributes String the attributes in the link |
| * @param text String the text allow with the link |
| * @param config MethodConfiguration |
| * |
| * @return LinkInfo |
| */ |
| public LinkInfo validateLink(MethodElement owner, String attributes, |
| String text, MethodConfiguration config) { |
| LinkInfo info = super.validateLink(owner, attributes, text, config); |
| |
| if (validateExternalLinks) { |
| |
| String url = info.getUrl(); |
| if ((url != null) && ResourceHelper.isExternalLink(url) |
| && !url.startsWith("ftp://")) //$NON-NLS-1$ |
| { |
| if (!validatedExternalLinks.contains(url)) { |
| Timer t = new Timer(); |
| try { |
| HttpResponse resp = HttpUtil.doGet(url, null, 6000); // timeout |
| // System.out |
| // .println(time |
| // + " mini-seconds querying Url '" + url + "', return |
| // status=" + resp.getStatus()); //$NON-NLS-1$ |
| // //$NON-NLS-2$ |
| } catch (java.net.UnknownHostException e) { |
| logInvalidExternalLink(owner, url, null); |
| } catch (Exception e) { |
| logInvalidExternalLink(owner, url, e.getMessage()); |
| } |
| |
| t.stop(); |
| time_for_external_link_checking += t.getTime(); |
| |
| // cache it |
| validatedExternalLinks.add(url); |
| |
| // do we need to log the info so that user know what external |
| // urls are referenced in the content? |
| logInfo(owner, t.getTime() + " mini-seconds " + NLS.bind(PublishingResources.externalUrl_msg, |
| url)); |
| } |
| |
| } |
| } |
| return info; |
| } |
| |
| /** |
| * log missing reference. |
| * @param owner MethodElement |
| * @param refElement MethodElement the missing element |
| */ |
| public void logMissingReference(MethodElement owner, |
| MethodElement refElement) { |
| super.logMissingReference(owner, refElement); |
| missingReferences.add(new MissingReference(owner, refElement)); |
| } |
| |
| /** |
| * log missing reference |
| * @param owner M<ethodElement |
| * @param guid String the guid of the missing element |
| * @param linkedText String the linked text. |
| */ |
| public void logMissingReference(MethodElement owner, String guid, |
| String linkedText) { |
| super.logMissingReference(owner, guid, linkedText); |
| missingReferences.add(new MissingReference(owner, guid, linkedText)); |
| } |
| |
| /** |
| * log missing resource. |
| * |
| * @param owner MethodElement |
| * @param resourceFile File |
| * @param url String |
| */ |
| public void logMissingResource(MethodElement owner, File resourceFile, |
| String url) { |
| super.logMissingResource(owner, resourceFile, url); |
| missingResources.add(new MissingResource(owner, resourceFile, url)); |
| } |
| |
| /** |
| * log invalid external link |
| * @param owner |
| * @param url String |
| * @param message String |
| */ |
| public void logInvalidExternalLink(MethodElement owner, String url, |
| String message) { |
| super.logInvalidExternalLink(owner, url, message); |
| invalidExternalLinks.add(new InvalidExternalLinkInfo(owner, url, |
| message)); |
| } |
| |
| /** |
| * get report about the content validation. |
| * |
| * @return XmlElement |
| */ |
| public XmlElement getReport() { |
| XmlElement reportXml = new XmlElement("validatorInfo"); //$NON-NLS-1$ |
| |
| if (invalidExternalLinks.size() > 0) { |
| String msg = time_for_external_link_checking / 1000 |
| + " seconds validating external links"; //$NON-NLS-1$ |
| System.out.println(msg); |
| logInfo(msg); |
| |
| XmlElement invalidExternalLinksXml = reportXml |
| .newChild("invalidExternalLinks"); //$NON-NLS-1$ |
| for (Iterator it = invalidExternalLinks.iterator(); it.hasNext();) { |
| InvalidExternalLinkInfo info = (InvalidExternalLinkInfo) it |
| .next(); |
| invalidExternalLinksXml.newChild("entry") //$NON-NLS-1$ |
| .setAttribute("url", info.url) //$NON-NLS-1$ |
| .setAttribute( |
| "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)) //$NON-NLS-1$ |
| .setAttribute("message", info.message); //$NON-NLS-1$ |
| } |
| } |
| |
| if (missingReferences.size() > 0) { |
| XmlElement invalidReferencesXml = reportXml |
| .newChild("invalidReferences"); //$NON-NLS-1$ |
| XmlElement missingReferencesXml = reportXml |
| .newChild("missingReferences"); //$NON-NLS-1$ |
| for (Iterator it = missingReferences.iterator(); it.hasNext();) { |
| MissingReference info = (MissingReference) it.next(); |
| if (info.refElement == null) { |
| invalidReferencesXml |
| .newChild("entry") //$NON-NLS-1$ |
| .setAttribute("element", info.linkedText) //$NON-NLS-1$ |
| .setAttribute("guid", info.guid) //$NON-NLS-1$ |
| .setAttribute( |
| "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ |
| } else { |
| missingReferencesXml |
| .newChild("entry") //$NON-NLS-1$ |
| .setAttribute( |
| "element", (info.refElement == null ) ? "" : LibraryUtil.getLocalizeTypeName(info.refElement)) //$NON-NLS-1$ |
| .setAttribute("guid", info.refElement.getGuid()) //$NON-NLS-1$ |
| .setAttribute( |
| "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| if (missingResources.size() > 0) { |
| XmlElement missingResourcesXml = reportXml |
| .newChild("missingResources"); //$NON-NLS-1$ |
| for (Iterator it = missingResources.iterator(); it.hasNext();) { |
| MissingResource info = (MissingResource) it.next(); |
| missingResourcesXml |
| .newChild("entry") //$NON-NLS-1$ |
| .setAttribute("url", info.url) //$NON-NLS-1$ |
| .setAttribute( |
| "resource", (info.resourceFile == null) ? "" : info.resourceFile.getPath()) //$NON-NLS-1$ //$NON-NLS-2$ |
| .setAttribute( |
| "owner", (info.owner == null) ? "" : LibraryUtil.getLocalizeTypeName(info.owner)); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| } |
| } |
| |
| long publishing_time = (Calendar.getInstance().getTimeInMillis() - publishing_start) / 1000; |
| int minutes = (int) publishing_time / 60; |
| int seconds = (int) publishing_time - minutes * 60; |
| |
| logInfo("Publishing time: " + minutes + " minutes " + seconds + " seconds"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| |
| return reportXml; |
| } |
| |
| /** |
| * add a category that should be published. |
| * |
| * @param e ContentCategory |
| */ |
| public void addValidCategory(ContentCategory e) { |
| if (!validCategories.contains(e)) { |
| validCategories.add(e); |
| } |
| } |
| |
| /** |
| * check if the element is discarded or not discarded elements will be |
| * treated as out side the configursation |
| * |
| * @param owner |
| * MethodElement the owner of the element |
| * @param feature |
| * Object EStructuralFeature or OppositeFeature |
| * @param e |
| * MethodElement the element to be checked |
| * |
| * @return boolean |
| */ |
| public boolean isDiscarded(MethodElement owner, Object feature, |
| MethodElement e) { |
| if (discardedElements.contains(e)) { |
| return true; |
| } |
| |
| // if the element is a ContentCategory and is not discarded |
| if (e instanceof ContentCategory) { |
| if (validCategories.contains(e)) { |
| return false; |
| } |
| |
| // otherwise, check if it should be discarded or not |
| // TODO |
| // for now, discard all referenced content categories if they are |
| // not included in the publishing view. |
| // NO!!!!!!!!!!!! This will lead to a lot of broken links and |
| // missing element |
| // TOO strong limitation. Let open it for now |
| // Publishing:Overview page in published website |
| // have broken links to BM |
| // return true; |
| return false; |
| } |
| return false; |
| } |
| |
| /** |
| * add a referenced element |
| * |
| * @param owner MethodElement |
| * @param e MethodElement |
| */ |
| public void addReferencedElement(MethodElement owner, MethodElement e) { |
| if (e == null) { |
| return; |
| } |
| |
| // don't add discarded elements |
| if (isDiscarded(owner, null, e)) { |
| if (debug) { |
| System.out |
| .println("Element is discarded: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ |
| } |
| return; |
| } |
| |
| if (e != null && !referencedElements.contains(e) |
| && !publishedElements.contains(e)) { |
| referencedElements.add(e); |
| logReference(owner, e); |
| } |
| } |
| |
| /** |
| * log a refernece |
| * |
| * @param owner MethodElement |
| * @param e MethodElement |
| */ |
| public void logReference(MethodElement owner, MethodElement e) { |
| if (debug) { |
| System.out |
| .println("--- Referenece Element Added: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * remove element from referenced list |
| * |
| * @param e MethodElement |
| */ |
| public void removeReferencedElement(MethodElement e) { |
| if (referencedElements.contains(e)) { |
| referencedElements.remove(e); |
| if (debug) { |
| System.out |
| .println("--- Reference Element Removed: " + LibraryUtil.getLocalizeTypeName(e)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * get all the referenced elements |
| * |
| * @return List |
| */ |
| public List getReferencedElements() { |
| return referencedElements; |
| } |
| |
| /** |
| * aet the discarded element for this publication. If an element is |
| * discarded, it should not be published and link to it should be link to |
| * mising element page |
| * |
| * @param e |
| * MethodElement |
| */ |
| public void setDiscardedElement(MethodElement e) { |
| |
| if (e == null) { |
| return; |
| } |
| |
| if (!discardedElements.contains(e)) { |
| discardedElements.add(e); |
| } |
| |
| // if th fdiscarded element is in the reference list, remove it as well |
| removeReferencedElement(e); |
| } |
| |
| /** |
| * check if an elenment is referenced or not. |
| * |
| * @param e MethodElement |
| * @return boolean |
| */ |
| public boolean isReferencedElement(MethodElement e) { |
| return (e != null) && referencedElements.contains(e); |
| } |
| |
| /** |
| * get the published elements |
| * |
| * @return List |
| */ |
| public List getPublishedElements() { |
| return publishedElements; |
| } |
| |
| /** |
| * set the default target for the referenced elements |
| * |
| * @param target MethodElement |
| */ |
| public void setTargetElement(MethodElement target) { |
| this.defaultTarget = target; |
| } |
| |
| /** |
| * check if there is a closure or not |
| * |
| * @return boolean |
| */ |
| public boolean hasClosure() { |
| return false; |
| } |
| |
| /** |
| * check if an element is in closure or not. |
| * |
| * @param e MethodElement |
| * @return boolean |
| */ |
| public boolean inClosure(MethodElement e) { |
| return true; |
| } |
| |
| /** |
| * add elements to closure |
| * |
| * @param items List |
| */ |
| public void addClosureElements(List items) { |
| // do nothing |
| } |
| |
| /** |
| * make a closure |
| * |
| */ |
| public void makeElementClosure() { |
| // do nothing |
| } |
| |
| /** |
| * get the flag on whether to show extra descriptor info. |
| * If true, information from linked element will be included in the descriptor page. |
| * |
| * @return boolean |
| */ |
| public boolean showExtraInfoForDescriptors() { |
| return showExtraInfoForDescriptors; |
| } |
| |
| /** |
| * set the flag |
| * |
| * @param show boolean |
| */ |
| public void setShowExtraInfoForDescriptors(boolean show) { |
| showExtraInfoForDescriptors = show; |
| } |
| } |